Redux:一个纯粹的状态管理系统,npm i redux -S
-
redux是一个独立的专门用于做状态管理的JS库,并不是react的插件库; -
redux可以用在React、Angular、Vue等项目中,但通常与React配合使用; - 功能类似于
Vue中的VueX,集中式管理组件之间的状态共享!
工作机制
-
createStore()创建一个指定reducer的store对象; -
storeRedux最核心的管理对象;- 内部维护着
store、reducer - 核心方法
getState()、dispatch(action)、subscribe(listener)
- 内部维护着
核心概念
-
action:标识要执行的行为对象,包括两个属性:type、xxxconst action = { type: 'INCREMENT', //标识属性,值为字符串,唯一,必要属性 data: 2 //数据属性,任意名称和类型,可选 } //创建action的工厂函数 const increment = (number) => ({type:'INCREMENT', data: number}) -
reducer:是一个纯函数,接收旧的state和action,返回新的state
之所以将这样的函数称为reducer,是因为它与被传入Array.prototype.reduce(reducer, ?initialValue)里的回调函数属于相同类型。
保持reducer纯净非常重要,函数返回一个新的状态,不要去直接修改原来的状态!所以永远不要在reducer里做如下操作:- 修改传入参数
- 执行有副作用的操作,如API请求和路由跳转
- 调用非纯函数,如
Date.now()、Math.random()
如果// reducers.js export default function counter(state=0, action) { //不要直接修改state,而是根据state的状态,返回一个新的state switch(action.type) { case 'INCREMENT': return state + action.data case 'DECREMENT': return state - action.data default: return state } }state是一个对象,recuder函数该如何处理呢?return { ...state, action.payload } -
store:将state、action与reducer联系在一起的对象;// store.js import { createStore } from 'redux' import reducer from './reducers' // 创建 `store` 对象 const store = createStore(reducer); export default store-
getState()获取state -
dispatch(action)分发action,触发reducer函数的调用,产生新的state -
subscribe(listener)注册监听,当产生新的state时,回调listener
import React from 'react'; import store from './redux/store' export default class Home extends React.Component { constructor(props) { super(props) } componentDidMount() { store.subscribe(() => { // 监听 store 的变化,手动强制更新组件视图 this.forceUpdate() }) } render() { console.log('Home: render') return( <div> <h3>{store.getState()}</h3> <button onClick={() => store.dispatch({type:"INCREMENT", data: 3})}>递增</button> <button onClick={() => store.dispatch({type:"DECREMENT", data: 1})}>递减</button> </div> ) } } -
优雅封装
创建目录 src/redux,用于存放 action-type.js、actions.js、reducers.js、store.js
-
reducers.js,reducer模块,包含n个reducer函数export function counter(state=0, action) { //赋予state一个初始值,state是一个数值 switch(action.type) { case 'INCREMENT': return state + action.data case 'DECREMENT': return state - action.data default: //首次初始化调用时,返回的一个初始值 return state } } // ... -
action-type.js存放常量字段export const INCREMENT = 'INCREMENT' export const DECREMENT = 'DECREMENT' -
actions.js包含所有action creatorimport { INCREMENT, DECREMENT } from './action-type' export const increment = (number) => ({type: INCREMENT, data: number}) export const decrement = (number) => ({type: DECREMENT, data: number}) -
store.js:包含所有生成的store对象;import { createStore } from 'redux' import { counter } from './reducers' const store = createStore(counter); //如果创建了多个store,则使用 export 导出 export default store -
App组件中导入actions.jsstore.dispatch(actions.increment(10)); // 0+10 store.dispatch(actions.decrement(2)); // 10-2
react-redux
react-redux是 react 的一个插件,降低 redux 与 react 的代码耦合度;
npm i react-redux --save
提供两个API:
-
Provider -为后代组件提供store -
connect(mapStateToProps, mapDispatchToProps) -连接组件与redux,为组件提供状态数据和变更方法。-
mapStateToProps回调函数,回调的是当前状态store.getState(),返回值会传给组件的props -
mapDispatchToProps对象,传递更新状态的方法,映射store dispatch(action)到组件的props上; - 返回一个高阶组件(函数),它会包装传入的组件,并把
mapStateToProps和mapDispatchToProps解构之后映射到组件的props中。
-
基本使用
-
index.jsimport { Provider } from 'react-redux' import store from './redux/store' ReactDOM.render(( <Provider store={store}> <App /> </Provider> ), document.getElementById('root')) -
App组件import { connect } from 'react-redux' import PropTypes from 'prop-types' import { increment, decrement } from './redux/actions' class App extends React.Component { constructor(props){ super(props) } static propTypes = { //声明接收的属性,类型校验 count: PropTypes.number.isRequired, increment: PropTypes.func.isRequired, decrement: PropTypes.func.isRequired, } render() { return (<div className="App"> <h2>I am App {this.props.count}</h2> <button onClick={() => this.props.increment(5)}>递增5</button> <button onClick={() => this.props.decrement(2)}>递减2</button> </div>); } } export default connect( // mapStateToProps 把store state映射到props中 state => ({ count: state }), // 回调参数state 也就是 store.getState() // mapDispatchToProps 把store dispatch(action)映射到props中的方法 { increment, decrement } )(App) - 使用高阶组件的装饰器简化
-
安装支持装饰器的插件
yarn add @babel/plugin-proposal-decorators --dev- 弹出默认配置,并配置
package.jsonyarn eject // package.json "babel": { "presets": [ "react-app" ], "plugins": [ [ "@babel/plugin-proposal-decorators", { "legacy": true } ] ] } - 注意:对于
VSCode -> 首选项 -> 设置,勾选Experimental Decorators
@connect( state => ({ count: state }), { increment, decrement } ) class App extends React.Component { // ... } export default App - 弹出默认配置,并配置
多个reducer函数
多个reducer函数,通过redux 提供的 combineReducers() 函数进行合并。
-
reducers.jsimport { combineReducers } from 'redux' function counter(state=0, action) { //赋予state一个初始值,state是一个数值 // ... } function comments(state=[], action) { //state是一个数组 switch(action.type) { case 'INCREMENT': // 不要直接修改state,而是根据state的状态,返回一个新的state return [action.data, ...state] case 'DECREMENT': //filter() 不会修改原数组state return state.filter((item, index) => index!==action.data) default: return state } } export default combineReducers({ count: counter, comment: comments }) -
combineReducers()让redux向外暴露的state是一个对象结构:{ count: 0, comment: [] }// store.js import reducers from './reducers' const store = createStore(reducers, applyMiddleware(thunk)); store.getState() // { count: 0, comment: [] } export default store -
App组件@connect( state => { return { count: state.count, comment: state.comment, } }, { increment, decrement } ) class App extends React.Component { //... render() { console.log(this.props.comment) return <div className="App">{this.props.count}</div> } }
异步事件
-
redux是个纯粹的状态管理器,默认支持同步,不支持诸如延迟、网络请求等异步处理,需要借助redux插件(异步中间件)npm i redux-thunk -S npm i redux-logger -S //日志中间件 -
createStore()支持第二个参数,应用中间件// store.js import { createStore, applyMiddleware } from 'redux' import thunk from 'redux-thunk' import logger from 'redux-logger' import { counter } from './reducers' // applyMiddleware() 应用中间件,根据中间件的传入顺序先后执行 // thunk, logger 的顺序不能乱,自动打印日志 const store = createStore(counter, applyMiddleware(thunk, logger)); export default store - 异步处理在
action中// actions.js export const increment = (number) => ({ type: INCREMENT, data: number }) /** * redux 本身不支持异步 export const incrementAsync = (number) => { setTimeout(() => { // 异步代码,会报错 return { type: INCREMENT, data: number } }, 1000) } */ //异步的 action 返回一个函数,由中间件去回调这个函数 export const incrementAsync = (number) => { return dispatch => { // 异步代码 setTimeout(() => { // dispatch({ type: INCREMENT, data: number }); dispatch(increment(number)); }, 1000) } }
redux的调试工具
- chrome浏览器:
redux-devtools - 依赖包:
npm i redux-devtools-extension --save-dev - 引入插件
// store.js import { composeWithDevTools } from 'redux-devtools-extension' const store = createStore(counter, composeWithDevTools(applyMiddleware(thunk)));













网友评论