1

众所周知,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组件之间的通信更加方便,也有利于状态的统一管理。


xiangzhihong
5.9k 声望15.3k 粉丝

著有《React Native移动开发实战》1,2,3、《Kotlin入门与实战》《Weex跨平台开发实战》、《Flutter跨平台开发与实战》1,2和《Android应用开发实战》