---
# System prepended metadata

title: コンポーネント指向な設計のハンズオン

---

# コンポーネント指向な設計のハンズオン
## Reactのデータの保持・受け渡しについて
### state
- コンポーネントはその内部状態をObject、Array、Number、String...でもつことができる
- コンポーネントが持つ内部状態は**state**という
- stateを変更するコールバック関数が作成され、これをコールバックすると新しいstateに更新される
- stateが更新されるとコンポーネントは再レンダリングされる
- パフォーマンスのため複数のstateの更新は非同期に行われる場合がある

*カウンターコンポーネントの例*
```jsx=
function Counter() {
    // useStateはstateを利用するためのReact Hookです。
    // 現在のカウンターの値: counterVal, 初期値: 0
    // counterValを変更するコールバック: setCounter
    const [counterVal, setCounter] = useState(0);
    
    // counterValを1増やす関数
    // onClickで実行されるときの引数はクリックしたターゲット
    const counterIncrement = () => {
        const currentVal = counterVal;
        setCounter(currentVal + 1)
    }
    
    // returnで返した内容がレンダリングされる
    return (
        <div>
            <p>現在の値は{counterVal}です。</p>
            <button onClick={counterIncrement}>1増やす</button>
        </div>
    );
    
}
```
## props
- あるコンポーネントが子要素としてコンポーネントを呼び出すときに、引数としてデータ(Object、Array、Number、String...)を与えることができる
- この与えるデータを**props**という
- propsが更新されるとコンポーネントは再レンダリングされる。

*プロフィールカードの例*
**親要素**
```jsx=
const parentData = 'abc';
return (
    <div>
        <Profile
            name="山田太郎"
            lang={['JavaScript', 'React']}
            parent={parentData} />
    </div>
);
```
**子要素**
```jsx=
function Profile(props) {
    console.log(props.parent) // 'abc'
    return (
        <section>
            <h1>{props.name}</h1>
            <ul>{props.lang.map((lang) => {
                return (<li key={lang}>{lang}</li>)
            })}</ul>
        </section>
    );
}
```
### データの受け渡し
- propsを使えば親要素から子要素にデータを渡すことができる
- propsはバケツリレーのように子要素の子要素、そのまた子要素と、子孫要素へと引き継ぐことができる(ただし階層が深すぎて渡すのが煩雑な場合は`useContext`フックや別のライブラリである**Redux**を利用すればかんたんにできる)
- stateの内容もpropsで子要素に渡すことができる
- 子要素から親要素へデータを渡すことはできないししてはいけない
    この場合、子要素のstateがデータを持つのではなく、親要素のstateがデータを持ち、親要素のstateの更新コールバック関数をpropsで子が渡すことで回避できる。つまり、**複数の子要素で同じデータを共有したいときは、その最も浅い共通の親要素のstateがもつべきである。**
- 要素が再レンダリングされるのは次の場合である
    ※ 要素とコンポーネントの違いに注意
    - stateが変更されたとき(=stateの変更コールバックが呼ばれたとき)、そのコンポーネントは再レンダリングされる
    - propsが変更されたとき({前のprops}!=={新しいprops}のとき)、その要素は再レンダリングされる
    - 親コンポーネントが再レンダリングされたとき、子コンポーネントは無条件で再レンダリングされる(ただし`useMemo`で回避できる場合あり)
### 設計のポイント
- データの移動は親=>子だけなので、変更が起こりうる、かつ複数の要素から参照されるデータは上の階層に保持すべき
- 親が再レンダリングされると子は無条件に再レンダリングされるので、データに無関係な子要素の再レンダリングを防ぐために変更のあるデータはできるだけ下の階層に保持し

という**相反する要求を満足するようにデータの構造、コンポーネントの構造を設計する必要がある。**
デザインやCSSの要求を満たすようにコンポーネントを設計するのも地味に大変。


## コンポーネントの階層構造概略
![](https://i.imgur.com/Uf8MgLy.png)
## コンポーネントのデータの移動
```mermaid
classDiagram
class PlayGround
PlayGround: gameState
PlayGround --> Board: gamecurrent
PlayGround --> Timeline: gameState
PlayGround --> ConnectionState: gameState.verify
PlayGround --> Chat
PlayGround --> GameInfo
Board --> Main: Koma array
Board --> SenteTegoma: Koma array
Board --> GoteTegoma: Koma array
Board --> Events: events
```
## データ構造例
```javascript=
rootGameData = {
    gameStateHistory = [
        {
            sfen: 'ABC...',
            csa: '+1234Fu',
            animation: 'Replace....'
        },
        {
            sfen: 'ABC...',
            csa: '+1234Fu',
            animation: 'Replace....'
        },
    ],
    current: {
        sfen: 'ABC...',
        csa: '+1234Fu',
        animation: 'Replace....'
        // currentはgetActiveの情報ももつ
    }
}
```