React全家桶之Redux

348 阅读6分钟
原文链接: mp.weixin.qq.com

什么是redux

        redux是一个流行的JavaScript框架,为应用程序提供一个可预测的状态容器。在标准的MVC框架中,数据可以在UI组件和存储之间双向流动,而redux严格限制了数据只能在一个方向上流动。

是专于状态管理的库,和react解耦;

单一状态,单项数据流;

核心概念 store 、action、reducer;

redux工作流

reactComponents 要改变store里的数据时:

01

先要派发一个action,action会通过store.dispatch(action)传递给store;

02

store会把旧的state和action转发给reducer;

03

reducer是一个函数,接收到旧的state和action,做一些处理之后,会返回一个新的state给store;

04

store用新的state替换掉旧的state;

05

store改变之后,reactComponents会感知store发生改变,这时组件从store里重新取数据,更新组件内容,页面就跟着发生变化了。

为了更好的理解redux工作原理,我们可以把redux工作流程比做借书过程。

reactComponents:借书人;

store:图书管理员;

action :借书人和图书管理员说的话;

reducer:图书管理员的查书手册。

当借书人(reactComponents)想要借一本书时,会和图书管理员(store)说一句话(action),告诉管理员要借什么书,这句话有固定格式,必须包含书名(type)和其它信息(value),图书管理员(store)听到话后会去查手册(reducer),手册(reducer)会根据之前的存书数据(previousState)和这句话(action),告诉图书管理员(store)现在的存书数据(newState)是什么,管理员(store)更新数据。

redux三项原则

唯一数据源

应用的状态数据应该只存储在唯一的store上。 这个唯一store上的状态,是一个树形的对象ObjectTree,每个组件往往只是用树形对象上一部分的数据。

保持状态只读

不能直接去修改状态,派发action是唯一改变store的方法。

数据改变只能通过纯函数完成

为了描述action如何改变ObjectTree,需要编写reducer,reducer必须是纯函数(函数的返回结果必须完全由参数state和action决定,不能修改参数state和action对象,而且不产生任何副作用。)

Redux小例子

我们用react+redux实现一个小功能:点击“+”、“-”按钮的时候,输入框里的数字对应的+1、-1。

在开始之前,确保已经安装了Node.js、npm包管理工具(或yarn);用create-react-app生成一个“redux-demo”项目;安装了redux库。(点进来看文章的人,这些应该是没有问题的哈,就不赘述了~)

实际项目中,我们习惯用一个store文件夹来管理store。文件夹下有四个文件,其中,actionTypes.js定义action类型;actionCreators.js定义action构造函数;reducer.js定义reducer函数;index.js来创建store。因为本例较简单,所以把四个文件的内容整合在了一个store.js文件中。

store.js:

import { createStore } from 'redux';//actionTypesconst ADD_ACTION='add_action';const MINUS_ACTION='minus_action';//actionCreatorsexport const addAction=()=>({    type:ADD_ACTION})export const minusAction=()=>({    type:MINUS_ACTION})//reducersconst defaultState={    value:0};const reducer=(state=defaultState , action)=>{    switch (action.type) {        case ADD_ACTION:            return {...state,value:state.value+1};        case MINUS_ACTION:            return {...state,value:state.value-1};        default:            return state;    }}//indexexport const store = createStore(reducer);

actionTypes:

定义action类型,把action类型定义为常量,有一个好处是方便出错后调试。如果action类型不用常量 ADD_ACTION而直接用字符串’add_action’,当组件中派发action时一不小心拼写错字符串,action不会正常派发,但是控制台是不会报错的。

actionCreators:

每个构造函数都返回一个action对象(使用redux中间件的时候action还可以是函数),包含type和value。

reducer:

reducer里定义了store的初始状态。

reducer函数中往往包含以action. type为判断条件的if-else或者switch语句。 reducer只用关心如何更新state,而不要管state怎么存。代码中使用了三个句点组成的扩展操作符,这表示把state中所有字段扩展开,而后面对value值对应的字段会赋上新值。

return {...state,value:state.value+1};

等同于

const newState = Object.assign({}, state);newState.value++;return newState;

像上面这样写,创造了一个newState完全复制了state,通过对newState的修改避免了对state的修改,不过这样写显得冗长,使用扩展操作符看起来更清晰简洁。

⚠️注意:reducer里不能修改state,reducer应该是纯函数,不能产生任何副作用。

index:

这个文件定义全局唯一的store。

接下来看如何在组件中获取和更新store中的数据。

app.js:

import React , { Component } from 'react';import { store , addAction , minusAction } from './store'class App extends Component{  constructor(props){    super(props);    this.state=store.getState();    this.add=this.add.bind(this);    this.minus=this.minus.bind(this);    this.listerner=this.listerner.bind(this);  }  componentDidMount(){    store.subscribe(this.listerner);  }  componentWillUnmount() {    store.unsubscribe(this.listerner);  }  render(){    return(        <div>            <button onClick={this.add}>+</button>            <input type="text" value={this.state.value}/>            <button onClick={this.minus}>-</button>        </div>    )  }  listerner(){    const value=store.getState().value;    this.setState({      value    })  }  add(){    store.dispatch(addAction());  }  minus(){    store.dispatch(minusAction());  }}export default App;

在app.js中我们要引入store。

constructor中初始化this.state的数据来源于store,通过store.getState()能够获得store上存储的所有状态。

在componentDidMount函数中,我们通过store的subscribe监听其变化,只要store状态发生变化,就会调用这个组件的listerner方法,从而保持this.state和store上的状态始终一样;在componentWillUnmount函数中,我们把这个监听注销掉,这个清理动作和componentDidMount中的动作对应。其实,这个增加监听函数的语句也可以写在构造函数中,但是为了让mount和unmount的对应看起来更清晰,在本例中我们把store的subscribe函数放在componentDidMount中。

改变store中状态的唯一方法是派发action,派发action就需要调用store.dispatch函数。在render函数中,对于点击“+”按钮和“-”按钮的onClick事件,被分别挂上了add函数和minus函数,所做的事情就是派发对应的action对象出去。

常用api

createStore :创建store。

const store = createStore(reducer);

store.dispatch :派发action。

store.dispatch(action)

store.getState :获取store里所有内容。

this.state=store.getState();

store.subscribe :监控store里的state是否改变。

store.subscribe(this.listener);

嗯,似乎再写一些redux实现原理的东西才算圆满,可是漂亮的小编有点瞌睡了,想来偷懒一下,下次再补也是可以的。🌚