Edited at

React v16で実装された new Context APIを使って、Reduxへ別れを告げる

alt

React v16でContext APIが新しくなりました。

このContextを使うことで、これまでサードパーティーのライブラリに頼っていたstate管理をreact本来の機能で実装できるようになりました。

しかしfacebookの例により、シンプルで柔軟性に富む代わりに実際の運用が難しいです。

それではreactのContext APIをstate管理に使う際のメリットデメリットについて書きます。

メリット

1. 記述量が圧倒的に少ない。reduxの1/3。

2. stateだけでなく、変数を渡せる。

3. 可読性が高くシンプルなコードが書ける。

4. 学習コストが低い。

5. react hookのuseReducerと共に使うとかなりスケールする。

デメリット

1. ContainerコンポーネントでJSXが純粋なViewでなくなり、ネストが2段深くなる。

2. 複数のstoreからcontextを利用しようとすると、記述が増える。

3. storeが一つでないのでスパゲッティになりやすい。

4. contextを親のstateに入れないと、renderが何回も呼ばれる。(後述)


Context APIの要素

Context APIは三つの要素のみで成り立ちます。


1. React.createContext

後述するProvider, Consumerをペアで作ります。

例 const {Provider, Consumer} = React.createContext;

// 以下のように名前をつけることもできます。

const RootCotext = React.createContext;


2. Provider

Consumerに、context(state)を渡す為のコンポーネントです。

reduxのprovider, createStoreの役割です。


3. Consumer

Providerからcontext(state)を受け取ります。

reduxのconnect,mapStateToProps,mapDispatchToPropsの役割です。


それでは実例を見てみましょう。

Text はReactNativeの要素で文字列を表示します。

従来はこのようにpropsをバケツリレーして渡していました。


// App.js
const Grandson = (props) => (
<Text>{props.name}</Text>
);

const Son = (props) => (
<Grandson name={props.name}/>
);

export default class Father extends Component {
render() {
return (
<Son name='カバ' />
);
}

次にContextを使って擬似global stateを実現してみます。

// App.js

import React, {Component} from 'react'
import {Text} from 'react-native'

// contextの作成。
const RootContext = React.createContext()

const Grandson = () => (
<RootContext.Consumer>
{context=>
<Text>{context.name}</Text>
}
</RootContext.Consumer>
)

const Son = () => (
<Grandson />
)

export default class Father extends Component {
render() {
return (
<RootContext.Provider value={{name: 'kaba'}}>
<Son />
</RootContext.Provider>
)
}
}

これで父コンポーネントから孫コンポーネントへstate(今回は変数)を渡すことができるようになりました。

このように擬似global変数を作れました。

ただ一つ注意点があって、valueに、オブジェクトを直に渡すと、画面描画時に、毎回オブジェクトが生成されメモリの無駄なので、

export default class Father extends Component {

state = {name: 'kaba'}

render() {
return (
<RootContext.Provider value={this.state}>
<Son />
</RootContext.Provider>
)
}
}

のように、contextはstateの中に入れておくとパフォーマンスの問題が起こりません。


結論

小規模アプリの開発には向いている。

非同期もasync, awaitで解決できそうだ。

combineReducerもどきも独自ルールで作れる。

しかし、それ以外の用途ではredux, mobxを使用した方が使い勝手がいい。

結局シンプルすぎて、独自ルールを追加しないとやっていけないのが辛い。

チーム開発では推奨できない。

だだし、疎結合なので、reduxへの移行も比較的簡単だ。

十分に実用的なので、個人では使っていきたい。

実務で使う場合は、以下の記事を参考にされたし!

React v16で実装された new Context APIを使って、Reduxへ別れを告げる②


Unstated

実は、このContext Apiを超薄く拡張し、実用レベルにまでしたライブラリがある。

unstatedだ。

reduxを捨ててunstatedを使う理由

2019年からreactを使う人はこれを使うといい。star5000越えで、今reactで一番人気のあるreduxの代用だ。

unstatedはわずか200行のライブラリだ。テストもしやすいのでベテランエンジニアにこそオススメしたい。

私は実務で使っている。