react nativeでglobal Stateの管理って何使ってますか? reduxですか?mobxですか?
両方とも堅牢なライブラリですが、記述量が多いし学習量も多いです。
今、reactを使ってる方、新しく入ってきた人、reduxについてこれてますか?
react初心者には上の二つは厳しいです。。。
今、海外ではぐんぐん伸びてきている超軽量state管理ライブラリ「unstated」使ってみませんか?
global stateをsetStateで変更するため、学習コストがとても低いです。
私はreduxからunstatedに乗り換えたら開発工数が半分になりました。
超おすすめです。
unstatedとは
- React標準のContextAPIを超薄く拡張したライブラリ。
- 学習コストが低く、シンプルでプロトタイプの作成に適している。
- わずか400行で書かれ、 サードパーティーのライブラリを使わず純粋なreactのみで書かれている。
- reactのみで書かれているので、テストが簡単で、ベテランエンジニアにはこれが嬉しい。
- 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,
})