27
19

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.

Reduxは諦めてhooksのuseContextでステート管理してみる(TypeScript使います)

Last updated at Posted at 2020-01-27

Reactを実装しはじめて、いよいよステートが色々なところに散らばってわけ分からなくなってきたので、Reduxを導入しようとしたら上手くいきませんでした。

そのため一旦Reduxは諦めてhooks(useReducer、useContext)を使うことにしたらやりたいことはできたし、とても簡単でしたので備忘録的に残しておきます。

useContext導入以前

sample1.tsx
export const Sample = () => {
  const [id, setId] = useState('');

  const eventHandler: (id: string) => void = id => {
    setState(id);
  };

return (
    <Container>
      <LeftContainer eventHandler={eventHandler} />
      <RightContainer id={id} />
    </Container>
  );
}

このような形でstateの中身やstateを変える関数を子要素に渡して、その子要素がさらに孫要素に渡して、、、(以下無限)みたいなことをしてました。いわゆるprop drillingっていうやつですね。

useContext導入後

useReducerも一緒に使って、親要素に全て集約し、その子孫のコンポーネントであればどこでも呼び出せるようにします。

reducer.ts
export type StateType = {
  id: string;
};

export type ActionType = {
  type: string;
  id: string;
};

export const myReducer = (state: StateType, action: ActionType) => {
  switch (action.type) {
    case 'setId':
      return { ...state, id: action.id };
    default:
      return state;
  }
};

親要素:

Parent.tsx

import React, { useState, createContext, useReducer } from 'react';
import { Child } from './Child';
import { myReducer, StateType, ActionType } from './reducer';

// contextの型を宣言
// stateはParentコンポーネントのステート
// dispatchはステートを変える関数(正確にはどの処理を行うか振り分けるためのもの)
type ContextType = {
  state: StateType;
  dispatch: React.Dispatch<ActionType>;
};

// ステートの初期値を宣言
const initialState = { id: '' };

// コンテキストを作成する
export const ParentContext = createContext({} as ContextType);

const Parent = () => {
// useReducerを使ってstateとdispatchを宣言。reducerは上記で作成済みのものを使う
  const [state, dispatch] = useReducer(myReducer, initialState);

  return (
    <ParentContext.Provider value={{ state, dispatch }}>
      <Child />
    </ParentContext.Provider>
  );
};

export default Parent;

retunで返しているJSX要素を見ていただけると分かりますが、Childコンポーネントにはpropsとして何も渡していません。

Child.tsx
import React, { useContext } from 'react'
import { ParentContext } from '../Parent';

const Child = () => {
// ここではstateとdispatch両方受け取っているが、必要な方だけ受け取ることも可能
  const { state, dispatch } = useContext(ParentContext);

  return(
    <div
       id="newId"
       onClick={(e) =>
          dispatch({
              type: 'setId', // reducerで指定したtypeを使う
              id: e.target.id,
           })
        }
    >
      {state}
    </div>
  );
}

親要素(先祖要素)から受け取ったcontextを使うことで、propsの受け渡しをせずに、子孫要素で作成したcontextを使うことができるようになりました!
ちなみにuseContextを使っても、propsは普通に子要素に受けわたすことができるので使い分けも可能です。

将来的に複雑なことをするかもと思い、Reduxの導入をチャレンジしたのですが、結果的には今はやりたいことがこれだけで十分でした。

学習コストがほぼ0なのと、Reduxのステートの管理とは干渉しないっぽいのでとりあえず導入してみるのがおすすめです。

ちなみに

Reduxの導入を諦めた理由は、バックエンドにGraphQLを使っていて、そのクライアントにapollo-clientを使用しているのですが、それがどうも干渉してる感じでした。(ここがわからず2日くらい上手くいかないと悩んでた)

apollo-clientのバージョン1ではReduxと併用できて、バージョン2でできなくなったものの、apollo-cache-reduxというライブラリを使えば可能ということだったのですが、apollo-cache-reduxは公式でもうメンテナンスしないみたいになってたので、結局Reduxは使えず。。。

Apollo Client自体でステートの管理もできそうな感じしてるのですが、とりあえず今回はuseContext使おうと思いました。

参考資料

Reduxから Context API with Hooks へ
【react hooks】useContextを使ってみた
React createContext issue in Typescript?
reduxでgraphqlを使う
apollo-cache-redux(github)

27
19
1

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
27
19

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?