188
141

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

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

Last updated at Posted at 2018-04-07

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と共に使うとかなりスケールする。
    デメリット
  6. ContainerコンポーネントでJSXが純粋なViewでなくなり、ネストが2段深くなる。
  7. 複数のstoreからcontextを利用しようとすると、記述が増える。
  8. storeが一つでないのでスパゲッティになりやすい。
  9. 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行のライブラリだ。テストもしやすいのでベテランエンジニアにこそオススメしたい。
私は実務で使っている。

188
141
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
188
141

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?