-
Redux
-
初识redux
redux在官网上的定义是A Predictable State Container for JS Apps。中文文档上解释为 Redux是Javascript状态容器,提供可预测化的状态管理。
在react中,数据在组件中是单向流动的,通过props从父组件流向子组件,当非父子组件之间进行通信就比较麻烦,,redux,mobx,RxJS等都是一些常用的方法,但是为了避免进行造轮子,如果你的项目中用不到状态管理,能不用redux就就不用redux,除此之外,react hooks是一个很好地选择。对于react hooks的一些信息和理解,我在之前也大致上说过一些,有兴趣的话可以去了解一下,mobx后期会继续跟进,本文主要讲述redux的相关内容,我们继续往下看。React-Hooks传送门: 点这里
-
redux设计理念和原则
-
redux设计理念
如图所示,redux是将整个应用状态存储到一个地方称为store,里面保存着一个状态的store tree,组件可以派发(dispatch)行为(action)给store,而不是直接通知其他组件,组件内部通过订阅store中的状态state来刷新自己的视图。
-
redux三大原则
-
单一数据源
整个应用的state被存储在一棵store tree中,并且这个tree只存在于唯一一个store中。
-
state是只读的
唯一改变state的方法就是触发action,action是用于描述一个已发生事件的普通对象
-
使用纯函数来执行修改
为了描述action如何改变state,你需要编写reducers
-
-
-
redux中主要的api
-
redux源码比较简单,源码以及注释加起来也就700行的样子,有兴趣的同学可以去研究研究,基本上花个半天时间差不多就明白了,redux暴露出来的主要的api有createStore,combineReducers,bindActionCreators,applyMiddleware,compose这些。
-
createStore(reducer,[preloadedState],enhancer)
-
作用
创建一个Redux store来存放应用中所有的state,应用中应有且仅有一个store
-
参数
- reducer(Function): 接收两个参数,分别是当前的state和要处理的action,返回新的state树
- [preloadedState](any):初始时的state,如果你使用combineReducers创建reducer,它必须是一个普通对象,与传入的keys保持同样的结构,否则,你可以自由传入任何reducer可理解的内容
- enhancer(Function):一个组合store creator的高阶函数,返回一个新的强化过的store creator,这与middleware相似。它允许你通过复合函数改变store接口
-
返回值
Store:保存了应用所有state的对象,改变state唯一的方法就是dispatch action,也可以subscribe监听state的变化,然后更新UI
-
-
combineReducers()
- 作用
- combineReducers所做的只是生成一个函数,这个函数用来调用一系列的reducer,每个reducer根据它们的key来筛选state中的一部分数据并处理,然后这个生成的函数再将所有reducer的结果合并成一个大的对象
- combinerReducers中包含所有的reducer都没有更改state,那么也就不会创建一个新的对象
- 作用
-
applyMiddleware(...middlewares)
-
作用
使用包含自定义功能的middleware来扩展redux功能。Middleware 可以让你包装 store 的 dispatch 方法来达到你想要的目的。同时, middleware 还拥有“可组合”这一关键特性。多个 middleware 可以被组合到一起使用,形成 middleware 链。其中,每个 middleware 都不需要关心链中它前后的 middleware 的任何信息。 Middleware 最常见的使用场景是无需引用大量代码或依赖类似 Rx 的第三方库实现异步 actions。这种方式可以让你像 dispatch 一般的 actions 那样 dispatch 异步 actions。
-
-
bindActionCreators(actionCreators,dispatch)
- 作用
- 将单个活多个actionCreator转化为dispatch(action)的函数集合形式,开发者不用再手动dispatch(actionCreators)
- 作用
-
compose(...functions)
- 作用
- 从右到左组合多个函数,这是函数式编程中的方法,为了方便,被放倒了redux中,当需要把多个store enhancer依次执行的时候,需要用到它
- 作用
-
-
redux的用法
- store.js
import {createStore,applyMiddleware} from './reducers' import rootReducers from './reducers' //处理异步 import thunk from 'redux-thunk' // createStore的第一个参数必须是一个reducer,如果是多个,请在reducers目录下先使用combineReducers全并之后再导出 exoirt default createStore(rootReducers, applyMiddleware(thunk) ) - reducers
//index.js 合并多个reducer并到处 import { combineReducers } from 'redux' import reducer1 from './reducer1' import reducer2 from './reducer2' export default combineReducers({reducer1,reducer2}) //reducer1.js 纯函数 接收state和action并返回新的state const initState = [{ id: 1, title: 'Apple', price: 8888.66, amount: 10 }, { id: 2, title: 'Orange', price: 4444.66, amount: 12 }] export default (state = initState,action) => { switch(action.type) { case 'increment': return state.map(item => { if(item.id ===action.payload.id) { item.amount += 1 } return item }) case 'decrement: return state.map(item => { if (item.id === action.payload.id) { item.amount -= 1 } return item }) default: return state } } - action
//actionType.js export default { INCREMENT: 'INCREMENT', DECREMENT: 'DECREMENT' } //index.js import actionType from './actionType' 定义action的行为 // action有两种写法 // 第一种写成一个对象,这是标准的action, 但是,问题是不方便传递动态参数 // export const increment = { // type: actionType.CART_AMOUNT_INCREMENT, // payload: { // id: 123 // } // } // 在工作中,常用的一种方式是使用actionCreator, 它是一个方法,返回一个对象,这个对象才是真正的action export const increment = (id) => { return { type: actionType.CART_AMOUNT_INCREMENT, payload: { id } } } export const increment = (id) => { return { type:actionType.INCREMENT, payload:{ id } } } export const decrement = (id) => { return { type:actionType.DECREMENT, payload:{ id } } } // 异步action, 使用redux-thunk 之后,就可以在actionCreator里return一个方法,这个方法的参数是dispatch export const decrementAsync = id => dispatch => { setTimeout(() => { dispatch(decrement(id)) }, 3000) } - index.js //入口
import React from 'react import {render} from 'react-dom' import App from './App' import store from './store' render( <App store={store} />, document.querySelector('#root) ) - App.js
import React, { Component } from 'react' import { List } from './components' export default class App extends Component { render() { return ( <div> <List store={this.props.store} /> </div> ) } } - List.js
import React, { Component } from 'react' import { increment, decrement ,decrementAsync} from '../../actions' export default class List extends Component { constructor () { super() this.state = { list: [] } } getState = () => { this.setState({ list: this.props.store.getState().cart }) } componentDidMount () { this.getState() this.props.store.subscribe(this.getState) } render() { console.log(this.state) return ( <table> <thead> <tr> <th>id</th> <th>商品名称</th> <th>价格</th> <th>数量</th> <th>操作</th> </tr> </thead> <tbody> { this.state.list.map(item => { return ( <tr key={item.id}> <td>{item.id}</td> <td>{item.title}</td> <td>{item.price}</td> <td> <button onClick={this.props.decrementAsync.bind(this, item.id)}>延时三秒减1</button> <button onClick={ () => { this.props.store.dispatch(decrement(item.id)) } }>-</button> <span>{item.amount}</span> <button onClick={ () => { this.props.store.dispatch(increment(item.id)) } }>+</button> </td> <td></td> </tr> ) }) } </tbody> </table> ) } }
- store.js
-
-
react-redux
-
简介
React-Redux 是 Redux 的官方 React 绑定库。它能够使你的 React 组件从 Redux store 中读取数据,并且向 store 分发 actions 以更新数据 -
react-redux api
- Provider
- 作用
- Provider 使组件层级中的 connect() 方法都能够获得 Redux store。正常情况下,你的根组件应该嵌套在 中才能使用 connect() 方法。
- 属性
- store (Redux Store): 应用程序中唯一的 Redux store 对象
- children (ReactElement) 组件层级的根组件。
- 作用
- connect(mapStateToProps,mapDispatchToProps)
- 作用
- 连接 React 组件与 Redux store。连接操作不会改变原来的组件类。反而返回一个新的已与 Redux store 连接的组件类。
- 属性
- mapStateToProps(function) : 它接受state作为参数,返回一个对象,这个对象有state里面的状态,代表UI组件的同名参数,它建立一个从store tree 到UI组件props对象的映射关系,执行后返回一个对象,里面每个元素就是一个映射
- mapDispatchToProps():用来建立 UI 组件的参数到 store.dispatch 方法的映射。它定义了哪些用户的操作应该当作 Action,传给 Store。
- 作用
- Provider
-
react-redux 使用
- index.js //入口文件
//还是上面的例子 import React from 'react' import { render } from 'react-dom' // Provider是react-redux提供的一个组件 import { Provider } from 'react-redux' import App from './App' import store from './store' render( // 一般就直接把这个组件放在应用程序的最顶层,这个组件必须有一个store属性,这个store属性的值就是咱们创建的那个store // 只要在最外层包裹了这个Provider, 那么所有后代组件都可以使用Redux.connect做连接 <Provider store={store}> <App /> </Provider>, document.querySelector('#root') ) - List.js //组件
import React, { Component } from 'react' // connect方法执行之后是一个高阶组件 import { connect } from 'react-redux' // 导入actionCreators import { increment, decrement } from '../../actions/cart' @connect(mapState, { increment, decrement }) // 直接第二个参数传递一个对象,这里面的对象就是actionCreators, 只要传入了actionCreators, 在组件内就通过this.props.actionCreator来调用,这样的话,在调用之后,那个actionCreator就会自动帮你把它内部的action dispatch出去 也可以使用 mapDispatchToProps //// const mapDispatchToProps = dispatch => { // return { // add: (id) => dispatch(increment(id)), // reduce: (id) => dispatch(decrement(id)), // } // } // @connect(mapState, mapDispatchToProps) class List extends Component { render(){ ///// //// . . . 和前面相同 . <button onClick={this.props.decrement.bind(this, item.id)}>-</button> <span>{item.amount}</span> <button onClick={this.props.increment.bind(this, item.id)}>+</button> } } // mapStateToProps, 这里的state实际上就store.getState()的值 const mapstate = (state) => { // 这里return了什么,在组件里就可以通过this.props来获取 return { list: state.list } } export default List
- index.js //入口文件
-
useSelector useDispatch
- 随着react hooks 提供了一些很好的api,react-redux也在7.1之后引入了Hooks风格类型的api ,useSelector主要是从redux的store中提取数据,useDispatch主要是返回redux store中对dispatch函数的引用。我们可以使用useSelector和useDispatch来代替connect的使用
- 还是上面的例子,我们做一些改动
//List.js import React from 'react' import {useSelector,useDispatch} from 'react-redux' function List (){ const List = useSelector(state => state.list) const dispatch = useDispatch() return ( // . . . .... . { list.map((item,index) => { return ( .... <button onClick={dispatch({type:'decrement'})}>-</button> <span>{item.amount}</span> <button onClick={dispatch(type:'increment')}>+</button> ) }) } // ) }
好了,在react项目中基本上也就会用到这几个api,还有些不太常用的就不在这里叙述了,小伙伴们也可以自己尝试去了解和实现一下。其实,dva的出现也为我们简化了开发体验,使用dva配合umi基本上就不需要我们手动引入react-router antd redux react-redux这些了,但是对于redux用户来说,这些基础也还是要熟练掌握的。
-