React の dispatch を分割して部分的にステートを書き換える

## ステートに object を入れると dispatch しにくい

例として、`<Main />`、`<Article />`、`<Section />` の階層構造を持つ React アプリケーションを考えます。

・`<Main />`
　・`<Article />`
　　・`<Section />`

React 以外のステート管理ライブラリを使わず、`useContext` によるグローバルステートも使わない場合、以下のようにステート管理を書けます。

```jsx
import { memo, useEffect, useState } from "react";

const Main = memo(() => {
  // useReducer でも可。
  const [main, dispatchMain] = useState({
    article: {
      section: {
        content: "initial section content"
        // 他のプロパティ
      }
      // 他のプロパティ
    }
    // 他のプロパティ
  });

  return <Article article={main.article} dispatchMain={dispatchMain} />;
});

const Article = memo(({ article, dispatchMain }) => (
  <Section section={article.section} dispatchMain={dispatchMain} />
));

const Section = memo(({ section, dispatchMain }) => {
  useEffect(() => {
    setTimeout(
      () =>
        dispatchMain((prevMain) => ({
          ...prevMain,
          article: {
            ...prevMain.article,
            section: {
              ...prevMain.article.section,
              content: "Section content is updated."
            }
          }
        })),
      3000
    );
  }, [dispatchMain]);

  return <>{section.content}</>;
});

export default Main;

```

[![Edit redundant-dispatch](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/redundant-dispatch-r7cbt?fontsize=14&hidenavigation=1&theme=dark)

`dispatchMain` を子コンポーネントに渡すことで、子コンポーネントからステートを書き換えられるようになりました。この例では簡単に `useState` を使用していますが、代わりに [useReducer](https://ja.reactjs.org/docs/hooks-reference.html#usereducer) を使って `reducer` 関数を自力が書けば、ステートの変化を `<Main />` からでも制御できます。

しかし…… `main.article.section.content` を書き換えるために `dispatchMain` を使うの、冗長ではないでしょうか？

```js
dispatchMain((prevMain) => ({
  ...prevMain,
  article: {
    ...prevMain.article,
    section: {
      ...prevMain.article.section,
      // ここを書き換えたいだけなのに、冗長な dispatch を書いている。
      content: "Section content is updated."
    }
  }
}));

```

そこで、`dispatchMain` から `dispatchArticle` を作り、さらに `dispatchArticle` から `dispatchSection` を作ります。

## dispatch を分割して部分的にステートを書き換える

以下のようにして、`dispatchArticle` と `dispatchSection` を作れます。

```js
const dispatchArticle = useCallback(
  (action) =>
    dispatchMain((prevMain) => ({
      ...prevMain,
      article:
        typeof action === "function" ? action(prevMain.article) : action
    })),
  []
);

const dispatchSection = useCallback(
  (action) =>
    dispatchArticle((prevArticle) => ({
      ...prevArticle,
      section:
        typeof action === "function" ? action(prevArticle.section) : action
    })),
  [dispatchArticle]
);

```

`main.article.section.content` を書き換えるコードは、以下のようにすっきり書けます。

```js
dispatchSection((prevSection) => ({
  ...prevSection,
  // すっきりと書ける。
  content: "Section content is updated."
}));

```

全体のコードはこちら
[![Edit splitted-dispatch](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/splitted-dispatch-szn4u?fontsize=14&hidenavigation=1&theme=dark)

## 「dispatch を分割するパターン」が広まってほしい

この「dispatch を分割するパターン」、私は好きなのですが、インターネット上には全く事例がない。素直にステート管理ライブラリを使うべきなのか、それとも React だけでシンプルにステート管理するべきなのか……。`useContext` でグローバルステートを作ると、レンダリングパフォーマンスの管理が難しいしな……。

「dispatch を分割するパターン」が認知されると、メリット・デメリットも見えてきて、うれしいですね。