Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
19
Help us understand the problem. What is going on with this article?
@_akira19

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

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)

19
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
_akira19
Twitter: @_akira19
bajji
2019年創業のスタートアップ 。感情日記SNS『Feelyou(フィールユー)』、ブロックチェーンを活用したSNS『bajji(バッジ)』を開発しています。

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
19
Help us understand the problem. What is going on with this article?