JS的状态管理库——redux

801 阅读4分钟

这是我参与8月更文挑战的第11天,活动详情查看:8月更文挑战

redux

之前在学习react的时候,兄弟组件之间传状态都是要通过父组件来进行传递。redux可以解决这个问题,它可以让某个组件的状态让其他组件可以随时拿到(共享),在一个组件需要改变另一个组件的状态的时候就可以用redux。但是由于redux的实现操作比较复杂,能不用就不用,但还是要学。

  • redux是一个专门用于做状态管理JS库(不是react插件库)
  • 作用:集中式管理react引用中多个组件共享的状态

一、redux工作流程

组件可以分发任务和传递之前的状态值给Reducers来进行处理,Reducers刚开始会初始化状态,然后再对状态进行加工,加工时根据旧的stateaction产生新的纯函数,处理之后把新的状态值返回给Store,进而传递给组件。在这里,store是把stateactionreducer联系起来的一个很重要的对象。

action(动作的对象)

  • type:标识属性,值为字符串,唯一且是必填
  • data:数据属性,值类型任意,可选

redux原理图.png

二、redux的使用

  1. 创建一个store

    src创建一个redux的文件夹,再在里面创建一个store.js;这个文件专门用于暴露一个store对象,整个应用只有一个store对象

    • 引入createStore,专门用于创建redux中最为核心的store对象
    • 引入为Count组件服务的reducer
    • 暴露store对象
    import { createStore } from 'redux'
    import countReducer from './count_reducer'
    export default store = createStore(countReducer);
    
  2. 有了store的同时也要有reducer

    redux中创建一个reducer.js文件,该文件是用于创建一个为某个组件服务的reducer,reducer的本质就是一个函数;reducer函数会接到两个参数,分别为:之前的状态(preState),动作对象(action);reducer有两个作用:初始化状态、加工状态

    最开始的状态值为undefine或为空时就要初始化状态;之后就从action对象这种获取type和data,这里用switch来对不同的type进行处理;

    const initState = 0;
    export default function countReducer(preState = initState, action) {
        const { type, data } = action;
        switch (type) {
            case 'increment': return preState + data
            default: return preState
        }
    }
    
  1. 创建actionCreator,在redux中创建一个actionCreator.js文件,用于创建一些要进行的动作,专门为组件生成action对象,记得要暴露;例如:

    export const createIncrementAction = data => ({ type: 'increment', data })
    

如果方法类型比较多,为了防止写错,可以创建一个文件专门来定义这些方法的变量名,同时也便于管理;例:export const INCREMENT = 'increment';

在组件中引入刚刚创建的store.js文件,通过store.getState就可以获取redux中所保存的状态;

// 纯react写的加法
increment = () => {
    const { value } = this.selectNumber;
    const { count } = this.state;
    this.setState({ count: count + parseInt(value) })
}
// 用了redux之后写的加法
increment = () => {
    const { value } = this.selectNumber;
    store.dispatch(createIncrementAction(value))
}

此时如果调用了这个函数,状态就会发生改变,但是在redux中,它只是帮我们管理状态,并不会更新页面;因此在这里需要手动去调用render(),于是我们就需要用到一个生命周期钩子——componentDidMount,它可以检测redux中状态的变化,只要变化就会调用render

componentDidMount() {
    store.subscribe(() => {
        this.setState({})
    })
}

如果在用生命周期钩子实现的话,在每个组件中都要写上面这段代码,为了避免这个麻烦,也可以在渲染App组件的index.js中包一个订阅的函数;

ReactDOM.render(<App />, document.getElementById('root'))
store.subscribe(() => {
    ReactDOM.render(<App />, document.getElementById('root'))
})

异步action

同步action在返回的时候是一个对象,而异步的action返回的时候是一个函数;原来同步的时候定时器写在组件里面的方法中,而异步的话

  1. 在组件中调用store.dispatch的时候传值和时间给action,

    store.dispatch(createIncrementAsyncAction(value, 500))
    
  2. 在action中调用定时器;

    export const createIncrementAsyncAction = (data, time) => {
        return () => {
            setTimeout(() => {
               dispatch({ type: INCREMENT, data })
            }, time);
        }
    }
    
  • 因为store本来只能接收的是一个对象而不是函数
  • 下载一个npm add redux-thunk库,在store中引入thunk,
  • 同时要在redux中多引入一个applyMiddleware
  • 暴露store:export default createStore(countReducer, applyMiddleware(thunk));