Reactでグローバルに変数を使いたい機会ができた。
そういえば使い始めのころにNativeでグローバル変数の利用に右往左往してました。
今は大分慣れてきたので色々な方法を考えてみます。
単純な変数として扱う
一番安易な方法としてグローバルオブジェクトへの追加を考える。
window
なりglobal
なり使えるグローバルオブジェクトを探してそこに入れる。
1 2 3 4 5 6 |
//global.js global.myValue = 100; //someComponent.js global.myValue++; console.log(global.myValue); //101 |
まあ動くけどさすがにどうかと思う、管理できる気がしない。
とりあえず変数汚染は防いで一応管理している感を出す。
1 2 3 4 5 6 7 8 9 10 11 |
//global.js export let variable = 0; export const setVariable = _v => { variable = _v; }; //someComponent.js import { variable, setVariable } from "../path/global.js"; console.log(variable); //0 setVariable(10); console.log(variable); //10 |
これらの方法ではReactが変更を感知できない。
コンポーネントの更新や再描画をしてくれないので用途は限定的です。
Reactライフサイクルに対応する
値を複数コンポーネントで扱いたければ共通の親コンポーネント(多くはRoot)でstate
を管理して使用コンポーネントへprops
をバケツリレーで渡す方法があります。
1 2 3 4 5 |
<treeCmp1 gv={this.state}> //ここのステートを子孫コンポーネントが使う <treeCmp2 {...this.props}> //使わなかったとしてもpropsを渡す <treeCmp3> <Button onclick={this.props.gv.addValue}>+</Button> //変更用の関数もpropsに {this.props.gv.value} |
特に難しいことを考えずに多くのパターンで使えます。
しかしアプリが大きくなったり細分化すると受け渡しが面倒ですし、propsのこの値ってどこのやつだよみたいになったりします。
Redux : hp, github
検索するといくらでも出てくるstateを一元管理するライブラリ。reduxで状態管理してreact-reduxでReactと連携するような感じだと思います。
これならグローバル変数もなにもない。
用途の割に学習することが多そうなのと個人でちょっとしたものを作るにはオーバースペックな気がするので使わなくていいなら使いたくないです。
React Context API : doc
v16.3.0 (March 29, 2018)で公式サポートされた機能。
Reduxでやっていたことの一部が公式APIでできるようになる。
管理したい値をコンテクストに任せて配信/購読を書くことが出来ます。
説明を読むだけだと少し複雑に見えますが実際に使ってみるとかなりシンプルです。
- 作成:
React.createContext(initialValue)
でコンテクスト作成 - 配信:
<Context.Provider>
コンポーネントで包んでvalue
を配信 - 購読1:
someClass.contextType
を設定してthis.context
から購読 - 購読2:
<Context.Consumer>
コンポーネントの子関数で購読、SFC(Stateless Functional Component)で使えるのと複数コンテクストを扱える - 購読3:v16.8.0 (February 6, 2019)で追加されたHooksの
useContext(Context)
を使う
unstated : github、unstated-next : github
unstatedはcontext APIのpolyfillであるcreate-react-contextを拡張したもの。
unstated-nextはReactの新しいAPI対応バージョン(なんと38行)。
大雑把に見るとContextをContainerとしてラップして使いやすくしている。
非常にシンプルな拡張でソースを読んでも時間がかからないのでContext APIの勉強がてらに読んでみるのもいいと思います。
所感
厳重で繊細な管理が必要とかでないならContext APIを使うのがいいと思う。
サードパーティーのライブラリを使わなくていいというのが最大の利点(パッケージを追加して使用するのって意外と面倒)。
自分でContext APIのコンテナを書いているとunstated-nextをみたとき「おお、なるほど」となりますが、まだ柔軟性が高い(本来良いこと)ように感じます。
小さいアプリならhook
やinitialState
を渡す形でなく、コンテナとコンテキストを提供するだけの疎な作りの方が管理しやすい気がする。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
import React from "react"; const initialState = {}; const useMyState = () => { const [state, dispatch] = React.useReducer((state, action) => { switch (action.type) { ... } }, initialState); return { state, dispatch }; }; export let Context = React.createContext(); export default ({ children }) => <Context.Provider value={useMyState()}>{children}</Context.Provider>; |
state
とaction
、Context
を一元管理して合わせてデフォルトで配信用コンポーネントを提供するシンプルコンテナ。テストをする気すらない。
1コンテナで管理できるような単純用途ならこれで十分。
あとRedux、これはもう本気で必要にかられたときに一気にやるくらいしか使うことがない気がしています。代替手段があるうちはきっと使わない。