LoginSignup
138
120

More than 3 years have passed since last update.

次の状態管理はReduxをやめてunstatedにする理由

Last updated at Posted at 2019-02-19

alt

react nativeでglobal Stateの管理って何使ってますか? reduxですか?mobxですか?
両方とも堅牢なライブラリですが、記述量が多いし学習量も多いです。
今、reactを使ってる方、新しく入ってきた人、reduxについてこれてますか?
react初心者には上の二つは厳しいです。。。
今、海外ではぐんぐん伸びてきている超軽量state管理ライブラリ「unstated」使ってみませんか?
global stateをsetStateで変更するため、学習コストがとても低いです。
私はreduxからunstatedに乗り換えたら開発工数が半分になりました。
超おすすめです。

unstatedとは

  1. React標準のContextAPIを超薄く拡張したライブラリ。
  2. 学習コストが低く、シンプルでプロトタイプの作成に適している。
  3. わずか400行で書かれ、 サードパーティーのライブラリを使わず純粋なreactのみで書かれている。
  4. reactのみで書かれているので、テストが簡単で、ベテランエンジニアにはこれが嬉しい。
  5. storeの永続化のためのunstated-persist(遅いが、、、) デバッグのためのunstated-debugなど、エコシステムが充実している。

実際に使ってみよう。

以下は純粋なreactで書かれたカウンターアプリだ。まだunstatedは入っていない。

class Counter extends React.Component {
  state = { count: 0 }
  increment = () => {
    this.setState({ count: this.state.count + 1 })
  }
  decrement = () => {
    this.setState({ count: this.state.count - 1 })
  }
  render() {
    return (
      <View>
        <Text>{this.state.count}</Text>
        <Text onClick={this.decrement}>-</Text>
        <Text onClick={this.increment}>+</Text>
      </View>
    )
  }
}

上のコードをunstatedを使って、Viewとロジックを分離してみる。

unstatedでは3つの概念がある。reactのcontextAPIを触ったことがある人ならすぐに馴染める。

1.Container
global stateを記述するコンポーネント。reduxとmobxでいうstore
グローバルなstateの変更と保管を担当。
つまり、巨大なJSONと、そのJSONを変更するためのメソッド群
setStateで、unstatedのstoreの中身のJSONを書き換える。

2.Subscribe
ContainerのメソッドやStateを、Subscribeで囲ったコンポーネントに渡す。

3.Provider
Providerで囲ったコンポーネントの中で、Subscribeを使えるようにするメソッド。

unstaed を使って書いた例

純粋なreactっぽく書ける。

import React from 'react'
import { Provider, Subscribe, Container } from 'unstated'
import {Text, View} from 'react-native'

class CounterContainer extends Container {
  state = {
    count: 0
  }

  increment() {
    this.setState({ count: this.state.count + 1 })
  }

  decrement() {
    this.setState({ count: this.state.count - 1 })
  }
}

function Counter() {
  return (
    <Subscribe to={[CounterContainer]}>
      {counter => (
        <View>
          <Text onClick={() => counter.decrement()}>-</Text>
          <Text>{counter.state.count}</Text>
          <Text onClick={() => counter.increment()}>+</Text>
        </View>
      )}
    </Subscribe>
  )
}

export default App = () => (
  <Provider>
    <Counter />
  </Provider>
)

 テストもとても簡単

unstatedは純粋なreact コンポーネントなので、以下のようにとてもシンプルにテストが書ける。
シニアエンジニアにはこれが嬉しいはず!

test('counter', async () => {
  let counter = new CounterContainer()
  assert(counter.state.count === 0)

  await counter.increment()
  assert(counter.state.count === 1)

  await counter.decrement()
  assert(counter.state.count === 0)
})

ちょっと残念なところ。

unstatedでは、Subscribeの外側では、containerのメソッドを使うことはできない。
つまり、componentDidMoutとかの中で、unstatedのstateを変更するには一工夫する必要がある。

つまりHOCを使う必要がある。
以下のようにコンポーネントをSubscribeで囲うのが一般的な使い方だ。

class Counter extends Component {
  componentDidMount() {
    this.props.counter.increment()
  }

  render() {
    const { counter } = this.props
    return(
      <View>
        <Text onClick={() => counter.decrement()}>-</Text>
        <Text>{counter.state.count}</Text>
        <Text onClick={() => counter.increment()}>+</Text>
      </View>
    )
  }
}

const CounterWrapper = () => (
  <Subscribe to={[CounterContainer]}>
    {counter => <Counter counter={counter} />}
  </Subscribe> 
)

export default CounterWrapper

react navigationnと一緒に使う時は、HOCの名前でroutingにマップされるのに注意。

もし、unstatedとreact-navigationを使って、画面が真っ白になるバグが出たら、名前が正しくimportされているか確認しよう。HOCを使うと、react navigationでは、画面が真っ白になるバグが起こりやすい。HOC react navigationでググって解決しよう。

以下は正しい例

const CounterWrapper = () => (
  <Subscribe to={[CounterContainer]}>
    {counter => <Counter counter={counter} />}
  </Subscribe>
)

export const HomeStack = createStackNavigator({
  CounterWrapper,
  ScreenB,
  ScreenC,
})
138
120
2

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
138
120