Reactで繰り返し項目の独立性を高めるとkeyの設定に悩む

## 概要

配列をもとにしてこんなビューを作りたいときに試行錯誤してコンポーネントの独立性を高められたけれど`key`の設定に悩んでいる、という話です。

```html
<ul>
  <li><span>#1 foo</span></li>
  <li><span>#2 bar</span></li>
  <li><span>#3 baz</span></li>
</ul>
```

どういう経緯で`key`の悩みに至ったか段階的に書いていきます。

## 以降に出てくるコード例の前提

出てくるコンポーネントは`Root`、`Parent`、`Child`の3つです。
構造はこんな感じ。

```xml
<Root>
  <Parent>
    <Child><!-- ここが配列をもとに繰り返される -->
  </Parent>
</Root>
```

使うデータはこんな感じ。

```js
class Content {
    constructor(id, text) {
        this.id = id;
        this.text = text;
    }
}

const contents = [
    new Content(1, 'foo'),
    new Content(2, 'bar'),
    new Content(3, 'baz')
];
```

## Step.1 まずは何も考えずに書いてみた

```js
const Root = ({ contents }) => <Parent contents={contents} />;

const Parent = ({ contents }) => <ul>
      {contents.map(x => <Child key={x.id} content={x}/>)}
    </ul>;

const Child = ({ content }) => <li>
      <span>#{content.id} {content.text}</span>
    </li>;

ReactDOM.render(<Root contents={contents} />, document.getElementById('root'));
```

`Parent`の定義に`Child`が含まれています。
つまり`Parent`は`Child`に依存しているということです。
この依存を解消したくなりました。

# Step.2 Rootで構造を定義してみた

```js
const Root = ({ contents }) => <Parent contents={contents}>
      {contents.map(x => <Child key={x.id} content={x}/>)}
    </Parent>;

const Parent = ({ children }) => <ul>
      {children}
    </ul>;

const Child = ({ content }) => <li>
      <span>#{content.id} {content.text}</span>
    </li>;

ReactDOM.render(<Root contents={contents} />, document.getElementById('root'));
```

`Parent`と`Child`の構造を`Root`で定義するようにしました。
これで`Parent`から`Child`への依存を解消できました。

この時点で、Reactでは`Parent`や`Child`のように独立したコンポーネントと`Root`のように構造化を司るコンポーネントに分けて設計した方がよいコードになりそうだなと思うようになりました。

ただ、これだと`Child`が`li`要素を持っていて親（`ul`要素を持つコンポーネント）の存在を暗に示しています。
また、`Parent`も`li`要素を持っていないので子の存在を暗に示しています。
`Child`から`Parent`へ`li`要素を移動させると独立性をもっと高められるような気がしました。

# Step.3 `Parent`と`Child`の独立性を高めた

```js
const Root = ({ contents }) => <Parent contents={contents}>
      {contents.map(x => <Child key={x.id} content={x}/>)}
    </Parent>;

const Parent = ({ children }) => <ul>
      {React.Children.map(children, x =>
          <li key={x.props.content.id}>{x}</li>
        )}
    </ul>;

const Child = ({ content }) => <span>#{content.id} {content.text}</span>;

ReactDOM.render(<Root contents={contents} />, document.getElementById('root'));
```

`Child`から`Parent`へ`li`要素を移動しました。
これで暗黙的な親子の気配を取り除けたのでそれぞれ独立性が高まったと思います。

しかし、`Parent`で`li`要素に`key`に設定している値が子コンポーネントの`props.content.id`となっています。
これは子コンポーネントに渡される`props`の詳細を知っていることになり、完全には依存を解消できていません。

# Step.4 `key`になり得る値を引き回すルールを決めた

```js
const Root = ({ contents }) => <Parent contents={contents}>
      {contents.map(x => <Child key={x.id} relayedKey={x.id} content={x}/>)}
    </Parent>;

const Parent = ({ children }) => <ul>
      {React.Children.map(children, x =>
          <li key={x.props.relayedKey}>{x}</li>
        )}
    </ul>;

const Child = ({ content }) => <span>#{content.id} {content.text}</span>;

ReactDOM.render(<Root contents={contents} />, document.getElementById('root'));
```

配列から作られる繰り返しのコンポーネントには`relayedKey`という名前で`key`と同じ値を設定するようにルールを決めました。
`children`を`map`するとき、`map`の前後でコンポーネントは1対1になるので`relayedKey`が`key`として使えます。

これで`Parent`も独立性が高まりました。

## もっとよい方法はないか？

Step.4で示した`relayedKey`ルールの導入が今私が考えられる最善の方法です。
とはいえ単に名前を取り決めるというルールで縛っているだけなので納得はしていません。

もっとよい方法はありませんかね？|･`ω･)ﾁﾗｯ