Help us understand the problem. What is going on with this article?

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行のライブラリだ。テストもしやすいのでベテランエンジニアにこそオススメしたい。
私は実務で使っている。

kaba
React Native製アプリのヤミーを運営しています。Expo製なので、expoを実務で使う前にインストールしてみて下さい。https://play.google.com/store/apps/details?id=com.kabainc.yummy&hl=ja
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
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  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
ユーザーは見つかりませんでした