0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

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

Last updated at Posted at 2021-07-15

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

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

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

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

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

dispatchMain を子コンポーネントに渡すことで、子コンポーネントからステートを書き換えられるようになりました。この例では簡単に useState を使用していますが、代わりに useReducer を使って reducer 関数を自力が書けば、ステートの変化を <Main /> からでも制御できます。

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

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

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

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

以下のようにして、dispatchArticledispatchSection を作れます。

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 を書き換えるコードは、以下のようにすっきり書けます。

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

全体のコードはこちら
Edit splitted-dispatch

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

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

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

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?