🐷

useContextとuseReducerで安全性を高める

2021/07/04に公開

Reactの状態管理

Reactの状態管理は様々です.
現在一番有名だと考えられるのはReduxですが,Recoilの登場などにより今後は両者が共存していくのかもしれません.
しかし現時点でuseContextとuseReducerでも十分な安全性が発揮できるのではないかと思ったので記事にさせていただきます.

環境

react@17.0.2
typescript@4.3.2
vite-plugin-windicss@1.0.2

使い方

useContextはデータをpropsとしてバケツリレーしなくても良いように,任意の階層下でグローバル変数のようにアクセスできます.
それにuseReducerを追加して値の変更を安全に行っていきたいと思います.

App.tsx
import React, {
  createContext,
  useReducer,
} from 'react';

export const UserContext = createContext<ContextType>({
  state: 'default name',
  dispatch: null,
});
type ContextType = {
  state: string;
  dispatch: any;
};


function App() {
  type Action = {
    type: 'CHANGE';
    user: string;
  };
  const initialUser: string = '';
  const reducer = (state: string, action: Action) => {
    // 前の状態を使いたい場合はstateを使う
    switch (action.type) {
      case 'CHANGE':
        return action.user;
    }
  };
  const [state, dispatch] = useReducer(reducer, initialUser);

  useEffect(() => {
    console.log('App.tsx: ページのレンダリング');
  }, []);

  return (
    <div className="App">
	<RecoilRoot>
	  // valueにstate, dispatchを渡す
	  <UserContext.Provider value={{ state, dispatch }}>
	    <Router>
	      <Switch>
		<Route path="/" exact component={Home}></Route>
		<Route path="/useContext" component={UseContext}></Route>
		<Route path="404" component={Page404} />
		<Route component={Page404} />
	      </Switch>
	    </Router>
	  </UserContext.Provider>
	</RecoilRoot>
    </div>
  );
}
export default App;

App.tsxの説明

まずcreateContextで階層化に渡すデータを定義します.
ここではuseReducerによりstateとdispatchを渡すのでそれらを定義します.

次にuseReducerを作成します.
useReducerは引数にreducerとinitialDataを取ります.

reducerは現在(state)と変更する状態(action)を受け取り,新しい状態を返します.
このactionは変更の種類(type)と変更後のデータ(user)を取ることができます.

そしてuseReducerはstateとdispatchを返します.
stateは現在の状態,dispatchは変更するための関数を表します.

それらをProviderを用いて下の層へ流します.


UseContext.tsx

import React, { useContext, useEffect } from 'react';
import { UserContext } from '../App';

const UseContext: React.FC = () => {
  const { state, dispatch } = useContext(UserContext);

  useEffect(() => {
    console.log('useContextがレンダリングされました');
  });
  return (
    <div>
      <h1>this is UseContext component</h1>
      <p>user name: {state}</p>
      <input
        type="text"
        value={state}
        className="text-gray-700"
        onChange={(e) =>
          dispatch({
            type: 'CHANGE',
            user: e.target.value,
          })
        }
      />
    </div>
  );
};

export default UseContext;

UseContext.tsxの説明

まずcreateContextで生成したContextをインポートします.

そしてコンポーネント内でuseContextを使用しstateとdispatchを受け取ります.
useContextの引数に先ほどインポートしたContextを指定します.

あとは表示する際はstateを,変更する際はdispatchを使用します.

その際にApp.tsxで定義したようにtypeとdataを渡します.

まとめ

このようにすればuseContextをどこからでも参照でき,どこからでも変更できるという状態はなくなり安全性が高まります.

記事に関して情報が間違っているというがありましたら,コメントいただけると幸いです.

Discussion