かもメモ

自分の落ちた落とし穴に何度も落ちる人のメモ帳

JavaScript (ES2015) Object の色々メモ

キーの名前とプロパティの変数名が同じなら省略できる

ES5
var cute = '星宮いちご';
var cool = '霧矢あおい';
var sexy = '紫吹蘭';

var soleil_es5 = {
  cute: cute,
  cool: cool,
  sexy: sexy,
};
console.log( soleil_es5 );
// { cute: '星宮いちご', cool: '霧矢あおい', sexy: '紫吹蘭' }

👇

ES2015
const cute = '星宮いちご';
const cool = '霧矢あおい';
const sexy = '紫吹蘭';
let soleil_es2015 = { cute, cool, sexy };
console.log( soleil_es2015 );
// { cute: '星宮いちご', cool: '霧矢あおい', sexy: '紫吹蘭' }

省略してるのとしてないのが入り交じるとチョイ混乱しそうだから各初期化で書き方統一したほうが良さそうな気もする。

メソッドの定義の方法

ES5までは method_name: function() { ... } の形でオブジェクトにメソッドを定義していたのが、ES2015からは method_name() { ... } という書き方で定義できる。

ES5
var soleil_es5 = {
  cute: cute,
  cool: cool,
  sexy: sexy,
  toString: function() {
    return 'ES5 Soleil ' + this.cute + ', ' + this.cool + ', ' + this.sexy;
  }
};
console.log( soleil_es5.toString() );
// ES5 Soleil 星宮いちご, 霧矢あおい, 紫吹蘭

👇

ES2015
const soleil_es2015 = {
  cute,
  cool,
  sexy,
  toString() {
    return `ES2015 Soleil ${this.cute}, ${this.cool}, ${this.sexy}`;
  }
};
console.log( soleil_es2015.toString() );
// ES2015 Soleil 星宮いちご, 霧矢あおい, 紫吹蘭

Objectのkeyを変数で指定する

ES5までは動的なkeyを使いたい時は、一度オブジェクトを作成してobj[key] = valueの形にする必要があったが、ES2015からは {[key]: value} の形で定義することができるようになった。

ES5
var key = 'pop';
var value = '夏樹みくる';

var es5Obj = {};
es5Obj[key] = value;
console.log('ES5', es5Obj);
// ES5 { pop: '夏樹みくる' }

👇

ES2015
const key = 'pop';
const value = '夏樹みくる';

const es2015Obj = {
  [key]: value,
};
console.log('ES2015', es2015Obj);
// ES2015 { pop: '夏樹みくる' }

キーを定義している [] 内で計算することもできる
e. g.

let i = 0;
const myObj = {
  [`index-${++i}`]: i * i,
  [`index-${++i}`]: i * i,
  [`index-${++i}`]: i * i,
};
console.log( myObj);
// { 'index-1': 1, 'index-2': 4, 'index-3': 9 }

 

ポエム

お仕事で関わってるプロジェクトで初めてのTypeScriptに触れてて

const hash = {[key: string]: number} = {foo: 1};

既存のコードでこんなのが出てきて、こはいかに?となり調べたらES2015で使えるObjectのkeyを変数で指定する方法で型指定してるとあり、keyを変数で指定するこの書き方を知らなかったので改めてES2015的なObjectを調べました。結果良い機会になりました。

しかし今関わっているプロジェクトがフロントだけでもReact + Redux + TypeScriptで通ってきてない技術が複合的にあるので一度に理解しようとしたり、ステップを抜かして学習しようとしたら挫折するパターンなんで着実に理解していきたいという思いがある一方、仕事できてねーって焦りがあるので学びが合って楽しいのですが心が折れないように気をつけないとという感じの日々です。

何かを変えたいと思ったらやはり関わる人を変えるか、環境を変えるかですね。
モダンフロントエンドマンになるぞー!と、春らしい意気込みするのであった。


[参考]

リズと青い鳥でドハマリして積んでたユーフォの小説一気読みしました🐡
大変良かった。なかよしコンビマジ尊い… 新作劇場版も楽しみ⭐

React 複数のタグを返して render したい

ReactのJSXで複数のタグを返すコンポーネントを作りたい時

JSXはルートが1つのタグでないとエラーになる

例がアレだけど、これはエラーになる

function ListData() {
  // トップの改装に複数のタグあるとエラーになる
  return (
    <li>星宮いちご</li>
    <li>霧矢あおい</li>
    <li>紫吹蘭</li>
  );
}

class List extends React.Component {
  render() {
    return (
      <ul>
        <ListData />
      </ul>
    )
  }
}

ReactDOM.render(
  <List />,
  document.getElementById('root'),
);

👇

Parsing error: Adjacent JSX elements must be wrapped in an enclosing tag.
SyntaxError: Adjacent JSX elements must be wrapped in an enclosing tag.

React.Fragment を使う

複数のタグが並列したエレメントをrenderしたい場合は<React. Fragment>で囲えばOK

function ListData() {
  return (
    <React.Fragment>
      <li>星宮いちご</li>
      <li>霧矢あおい</li>
      <li>紫吹蘭</li>
    </React.Fragment>
  );
}

<React.Fragment>はDOMとして出力されないので、エラー回避のために<div>で囲って余計なタグを出力してしまうこともない。 React.Fragment
<></>というショートタグもあるけど、まだサポートされていないのが多いみたい。
追記 もう十分サポートされてるので <></> を使うのが良さそうです!

ループで作成したものだとエラーにならない

JSXを直接returnする際にトップの階層にタグが複数あるとエラーになるけど、map()などのループで作成した配列として返せばエラーにならないっぽい。

function ListData() {
  const data = ['星宮いちご', '霧矢あおい', '紫吹蘭'];
  const listData = data.map((val, i) => {
    return (<li key={i}>{val}</li>);
  });
  // エラーにならない
  return listData;
}

 
JSXキモい派だったけど、最近慣れてきた… 使っていれば慣れるものですね。


[参考]

React.js&Next.js超入門

React.js&Next.js超入門

React ソートしたときの key にハマる

React入門初心者マンとして引き続きエクセル的なモノを作るチュートリアルをやっています。
今回テーブルをソートしようとしてハマったのでメモ。

react: ^16.8.3
react-dom: ^16.8.3

リストのような配列から作られるエレメントには key を指定する

V-DOMはDOMと同じツリー構造になっていて、構造を比較して違いがあるとその箇所を実際のDOMに反映させる仕組み。
リスト構造は変更があっても、リスト内のどこに変更があったのか判断できない。
keyが設定されていると、keyを頼りにどこが変更されたのかをReactが判断できるので、更新の反映が最小限になるって事っぽい。

配列を.mapして作成するようなエレメントはkeyを付けないとwarningになる
e. g.

import React, {Component} from 'react';
import ReactDOM from 'react-dom';
const data = [
  ['0315', '星宮いちご'],
  ['0131', '霧矢あおい'],
  ['0803', '紫吹蘭'],
];

class List extends Component {
  render() {
    const list = this.props.data.map((row, i) => {
      return(<li>ID[ {row[0]} ]: {row[1]}</li>);
    });
    return (<ul>{list}</ul>);
  }
}

ReactDOM.render(
  <List data={data} />,
  document.getElementById('app')
);

keyを指定するようにワーニングが出る
Warning: Each child in a list should have a unique "key" prop.

map()で作られるリスト要素にkey属性を付ければOK

class List extends Component {
  render() {
    const list = this.props.data.map((row, i) => {
      return(<li key={i}>ID[ {row[0]} ]: {row[1]}</li>);
    });
    return (<ul>{list}</ul>);
  }
}

コンポーネントな時はmap()で内にある側だけにkeyを設定すれば良い

function ListItem(props) {
  const row = props.data;
  // こちらに key を設定する必要はない
  return (<li>ID[ {row[0]} ]: {row[1]}</li>);
}

class List extends Component {
  render() {
    const list = this.props.data.map((row, i) => {
      return(<ListItem key={i} data={row} />);
    });
    return (<ul>{list}</ul>);
  }
}

map()で作っている所にkeyが無ければワーニング

function ListItem(props) {
  const row = props.data;
  return (<li key={props.idx}>ID[ {row[0]} ]: {row[1]}</li>);
}

class List extends Component {
  render() {
    const list = this.props.data.map((row, i) => {
      // ここに key がないとダメ
      return(<ListItem idx={i} data={row} />);
    });
    return (<ul>{list}</ul>);
  }
}

👉 Warning: Each child in a list should have a unique "key" prop.

keyは変わらないものを指定する

ソートなどで並び順が変わるようリストの場合、keyindexなどにしていると変更内容が render されない場合がある

sample

See the Pen React List Element Sort TEST by KIKIKI (@chaika-design) on CodePen.

🙅‍♂️keyの指定がなかったり、indexがしていされているとソートされないパターン

リスト内のコンポーネントstateを持つ場合、リストのkeyの指定がなかったり、keyがindexになっているとソートでデータの並び順が変わっても正しくrenderされない

const data = [
  ['0315', '星宮いちご'],
  ['0131', '霧矢あおい'],
  ['0803', '紫吹蘭'],
];

// state を持つコンポーネント
class ListContentWithState extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      data: this.props.data,
    }
  }
  render() {
    const data = this.state.data;
    return (<span>ID[ {data[0]} ]: {data[1]}</span>);
  }
}

class List extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      data: this.props.initData,
      descending: false,
    }
  }

  _onSort() {
    const data = this.state.data.slice();
    const descending = !this.state.descending;
    console.log('> sort', (descending? 'DESC':'ASC'));
    data.sort((a, b) => {
      if( descending ) {
        return a[0] < b[0]? 1:-1;
      } else {
        return a[0] > b[0]? 1:-1;
      }
    });
    this.setState({
      data: data,
      descending: descending,
    });
  }

  _onReset() {
    console.log('> reset');
    this.setState({
      data: this.props.initData,
      descending: false,
    });
  }

  render() {
    console.log('> render', this.state.data);
    const list = this.state.data.map((row, i) => {
      // keyが無かったり、indexのようにソート時に変わってしまうものだと、
      // stateが変更されてもrenderされない
      return(<li key={i}><ListContentWithState data={row} /></li>);
    });
    return (
      <div>
        <button onClick={(e)=>this._onSort()}>SORT</button>
        <button onClick={(e)=>this._onReset()}>RESET</button>
        <ul>{list}</ul>
      </div>
    );
  }
}

ReactDOM.render(
  <List initData={data} />,
  document.getElementById('app')
);

React Unsortable List Element

👇 keyの指定をソートしても変わらないものにすればOK

class List extends React.Component {
  // ...
  render() {
    const list = this.state.data.map((row, i) => {
      // ソートしても変わらないkeyを指定すればOK
      const listID = row[0];
      return(<li key={listID}><ListContentWithState data={row} /></li>);
    });
    // ...
  }
}

React Sortable List

なんとなくですが、リスト内の子コンポーネント(ListContentWithState)がstateを持っている場合、子コンポーネント作成時にまずkeyが渡されて、そのkeyに該当するデータからstateを返しているから、最初の並びの順番で子コンポーネントが返され親コンポーネント(List)のstate.dataの順番は変更されているのに、renderはその順番で出力されないい。という感じなのではないかという印象です。

なので、子コンポーネントstateを持たなければ、親コンポーネントのリストのkeyは指定がなくても、ソート時に変更されてしまうindexでもソートされた内容の通りにrenderされます。が、恐らくkeyの指定がありReactが変更箇所だけをうまく変更してくれるより処理は重いのではないかと思います。
👇

🙆‍♂️keyの指定がなかったり、indexで指定されていてもソートされるパターン

1. リストエレメントの内容が直接書かれている場合
class List extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      data: this.props.initData,
      descending: false,
    }
  }

  _onSort() {
    const data = this.state.data.slice();
    const descending = !this.state.descending;
    console.log('> sort', (descending? 'DESC':'ASC'));
    data.sort((a, b) => {
      if( descending ) {
        return a[0] < b[0]? 1:-1;
      } else {
        return a[0] > b[0]? 1:-1;
      }
    });
    this.setState({
      data: data,
      descending: descending,
    });
  }

  _onReset() {
    console.log('> reset');
    this.setState({
      data: this.props.initData,
      descending: false,
    });
  }

  render() {
    const list = this.state.data.map((row, i) => {
      //  リストエレメントの key が index / 無し でもソートは反映され描画される
      return(<li key={i}>ID[ {row[0]} ]: {row[1]}</li>);
    });
    return (
      <div>
        <button onClick={(e)=>this._onSort()}>SORT</button>
        <button onClick={(e)=>this._onReset()}>RESET</button>
        <ul>{list}</ul>
      </div>
    );
  }
}

ReactDOM.render(
  <List initData={data} />,
  document.getElementById('app')
);
2. リストエレメント内にコンポーネントを持っているがstateを使っていない場合

リスト内にあるコンポーネントstateを持っていないならkeyは無くてもindexを指定していてもソートがrenderされる

// state を持たないコンポーネント
function ListContent(props) {
  const data = props.data;
  return (<span>ID[ {data[0]} ]: {data[1]}</span>);
}

class List extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      data: this.props.initData,
      descending: false,
    }
  }

  _onSort() {
    const data = this.state.data.slice();
    const descending = !this.state.descending;
    console.log('> sort', (descending? 'DESC':'ASC'));
    data.sort((a, b) => {
      if( descending ) {
        return a[0] < b[0]? 1:-1;
      } else {
        return a[0] > b[0]? 1:-1;
      }
    });
    this.setState({
      data: data,
      descending: descending,
    });
  }

  _onReset() {
    console.log('> reset');
    this.setState({
      data: this.props.initData,
      descending: false,
    });
  }

  render() {
    const list = this.state.data.map((row, i) => {
      // state を持たないコンポーネントなら key が index でもOK
      return(<li key={i}><ListContent data={row} /></li>);
    });
    return (
      <div>
        <button onClick={(e)=>this._onSort()}>SORT</button>
        <button onClick={(e)=>this._onReset()}>RESET</button>
        <ul>{list}</ul>
      </div>
    );
  }
}

ReactDOM.render(
  <List initData={data} />,
  document.getElementById('app')
);

key

  • map()で作られるようなリストエレメントにはkeyを設定したほうがパフォーマンスが良い
  • key はリスト内でユニークなものにする
    key は同じ反復するエレメント内だけでユニークであればよく、globalとしてユニークである必要はない
  • key は React自身のツリー構造のチェックに使われる
  • 明示的にkeyを指定しない場合はindexがデフォルトとして使用される
  • keypropsとして渡されない
  • 並び順が変わるようなリストの場合indexkeyにするとパフォーマンスが悪く、stateに問題がでる場合もあるので良くない

まとめ

Reactは頑張って英語の本家ドキュメントを読もう! (変化が早いのでそれが確実な近道...)

失敗を恐れるな! アイカツ格言 第17話


[参考]

React入門 React・Reduxの導入からサーバサイドレンダリングによるUXの向上まで (NEXT ONE)

React入門 React・Reduxの導入からサーバサイドレンダリングによるUXの向上まで (NEXT ONE)