众所周知,React中数据通信是单向的,即父组件可以通过props向子组件传递数据,而子组件却不能向父组件传递数据。要实现子组件向父组件传递数据的需求,需要父组件提供一个修改数据的方法,当页面越来越多的时候,数据的管理就会变得异常复杂。
并且,每次数据的更新都需要调用setState,特别是涉及到跨组件通信的问题就会很麻烦。在React开发中,为了解决跨组件通信的问题,业界开发了一大批状态管理框架,目前比较常用的React状态管理框架有Flux、Redux和Mobx等几个。
其中,Flux是Facebook用于建立客户端Web应用的前端架构,它利用一个单向数据流的方式补充了React的组合视图组件,解决了MVC技术架构中数据流管理混乱的问题。Redux则是由Dan Abramov开源的一款前端状态管理框架,Redux框架由Action、Store和Reducers三部分组成,所有组件的数据都存储到Store对象中,每个组件只需要改变Store中的数据,当Store数据发生变化时就会其他订阅的组件执行数据更新。Mobx是一个面向对象的状态管理框架,它与Redux的最大区别是可以直接修改数据,精准的通知UI进行刷新,而不是Redux的广播。
可以发现,Redux特别适合用在需要集中式管理数据场景中。多个组件使用同一个数据源,维护同一个数据样本,进而保持各个组件之间数据的一致性。react-redux是Redux状态框架在React中的技术实现,对于熟悉Redux状态管理框架开发者来说,学习react-redux将会显得非常容易。
在Redux状态框架中,Redux将状态管理分为Action、Store和Reducers三部分。其中,Redux将应用程序的状态存储到Store中,组件通过dispatch()方法触发Action,Store接收Action并将Action转发给Reducer,Reducer根据Action类型对状态数据进行处理并将处理结果返回给Store,其他组件通过订阅Store状态的来刷新自身的状态,整个框架的工作流程如图3-9所示。
下面以计数器为例来说明Redux的基本使用。首先,创建一个action.js文件,用来存放Action行为事件,如下所示。
export const ADD = 'ADD'
export const MINUS = 'MINUS'
然后,创建一个reducer.js文件,用来处理业务逻辑的更新,并将处理的结果返回给Store,如下所示。
import {ADD, MINUS} from './action';
function reducer (state = {count: 0}, action) {
switch(action.type) {
case ADD:
return {count: state.count + 1}
case MINUS:
return {count: state.count - 1}
default:
return state
}
}
export default reducer;
Reducer是一个纯函数,接收State和Action两个参数。其中,State是旧的状态,不可以直接修改,Reducer会根据Action的类型来生成不同的新State,并将结果返回给Store。
接下来,创建一个全局的Store对象,用来存放应用的状态数据,创建时需要使用Store提供的createStore()方法,如下所示。
import { createStore } from 'redux'
import reducer from './reducer';
const store = createStore(reducer)
export default store
除了createStore()方法外,创建的Store还有以下几个方法可以调用。
- getState():获取最新的状态数据。
- dispatch():派发Action行为事件。
- subscribe():订阅Store中的状态变化。
为了实现计数器加减的功能,还需要在组件的生命周期函数中添加订阅事件,并在组件销毁时解决订阅,如下所示。
class CounterPage extends React.Component {
constructor(props){
super(props)
this.state = {
number: store.getState().count
}
}
componentDidMount () {
this.unSubscribe = store.subscribe(() => {
this.setState({
number: store.getState().count
})
})
}
componentWillUnmount () {
this.unSubscribe && this.unSubscribe()
}
render() {
return (
<View style={styles.ct}>
<Text>{this.state.number}</Text>
<Button
title="加1"
onPress={() => store.dispatch({type: 'ADD'})}/>
<Button
title="减1"
onPress={() => store.dispatch({type: 'MINUS'})}/>
</View>
);
}
}
const styles = StyleSheet.create({
ct: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
});
export default CounterPage;
在上面的代码中,我们通过store.getState()方法来获取最新的State,而执行加减操作时通过store.dispatch()方法派发Action给Store。可以发现,在类组件中使用Redux还是挺繁琐的,需要开发者自己管理组件的状态数据,而如果改用React Hook就要简单许多。
在React Hook中使用Redux需要使用react-redux库提供的useSelector()与useDispatch()两个函数。其中,useSelector()函数可以用来获取状态值,而useDispatch()则可以用来修改状态数据,如下所示。
import { useSelector, useDispatch } from 'react-redux'
const CounterPage = () => {
const count = useSelector(state => state.count)
const dispatch = useDispatch()
return (
<View style={styles.ct}>
<Text>{count}</Text>
<Button
title='加1'
onPress={() => dispatch({type: 'ADD'})}/>
<Button
title='减1'
onPress={() => dispatch({type: 'MINUS'})}/>
</View>
);
}
const styles = StyleSheet.create({
…. //省略代码
});
export default CounterPage
可以发现,相比于类组件来说,使用React Hook实现就要简洁许多。首先,我们使用useSelector()函数获取Store中的状态,然后再使用useDispatch()函数派发事件。
最后,使用Redux在让不同组件之间共享状态数据时,还需要使用react-redux库提供的Provider包裹应用组件,如下所示。
const App = () => {
return (
<Provider store={store}>
<CounterPage />
</Provider>
);
};
重新运行代码,就实现了计数器的功能,如下图所示。
最后,需要说明的是,使用Redux进行状态管理时,应注意以下几点:
• 应用中有且仅有一个Store,该Store存储了整个应用的状态。
• State是只读的,修改State只能通过派发Action事件,为了描述Action改变State的过程,需要使用Reducer纯函数。
• 单一数据源让多个React组件之间的通信更加方便,也有利于状态的统一管理。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。