React状态管理 - Redux&React-redux使用详解教程
一、组件间传值方案(组件间通信)
了解 React 的朋友应该都知道React是状态数据驱动的框架。React组件组件之间传值有很多种情况,但是组件之间传值只有通过props,context,回调函数 来实现(不借助外部类库)。
- 父组件传值给自组件 ( props属性 )
- 自组件传值给父组件 ( 回调函数 )
- 兄弟组件传值 (需要传递的值传给 共有的父组件,然后兄弟组件通过props从父组件获取传递值)
上面是一些最常用的情况,但是如果在大型项目中,组件嵌套四五层层甚至更多,这时候有个需求说把第4层的一个值传给另一个关系甚远的堂兄组件,怎么搞?如果一层层的向上传值找到公共的祖先组件然后再通过props一层层的传?
这个时候如果有一个仓库能够帮我们管理这些状态,组件直接的修改值,获取值直接通过这个仓库来统一管理,再也不用像上面一层层的传值,是不是很爽?
二、状态集中管理仓库 Redux
根据redux的工作流程画了个简单的流程图。接下来我们根据流程图来说下redux数据流是怎么流转的。- store 数据仓库
- reducer 仓库管理员
- action 修改数据的信号
- view component 项目所有的组件
1.工作流程
首先我们需要创建一个仓库store,仓库里面可以存放组件们需要的数据state。
store仓库创建需要招聘处理外部动作信号的仓库管理员reducer。
reducer 管理员由 store 直接管理,reducer 管理员通过action 动作传递的信号的type来做相应的提前预定好的动作来管理store中的数据。
action 信号由数据用户view component 来触发,通过store 提供的联系方式 store.dispatch() 来发送action。
view component 可以通过store.getState()来获取仓库的所有数据,从而来提取自己需要的数据。 如果view component 希望store中的数据有改动的时候就通知自己,那么就需要订阅subscribe 仓库store,也就是在仓库的通知列表里面填一下联系方式,当仓库数据修改的时候,仓库会给订阅过的view comonent发送最新的数据。
2.代码实践
接下来我将写代码模拟父母挣钱,孩子索要零花钱来改变家庭资金的过程。
- 创建React项目(create-react-app)
javascriptcreate-react-app redux-demo //脚手架安装 npm install -g create-react-app
- 创建store文件夹
- 初始化 store 仓库
javascript//store.js import { createStore} from 'redux'; import reducer from './reducer' //初始化工厂数据 state const initState = { money: 0 }; //创建store工厂 const store = createStore( reducer, initState, ); export default store;
- store初始化需要reducer管理员
javascript//reducer.js //仓库管理员,接受action发送的信号,处理返回结果数据 const reducer = (state = {}, action) => { let newState; switch (action.type) { case 'SAVA_MONEY'://挣钱 newState = Object.assign({}, state); newState.money += action.payload return newState; case "OUT_MONEY"://取钱 newState = Object.assign({}, state); newState.money -= action.payload return newState; } return state; } export default reducer;
- 组件获取仓库中的数据state
javascript//Family.jsx import React, { Component } from 'react'; import store from './store/store'; import { saveMoney, outMoney } from './store/action' class Couple extends Component { constructor(props) { super(props); this.state = store.getState() store.subscribe(() => { let newState = store.getState(); //重新触发render,从而通过store.getState()获取到新值 this.setState(newState); }) } render() { const { money } = this.state; return ( <div> <p>Family Money:{money}</p> Couple: <button onClick={() => { const saveMoneyAction = saveMoney(100) store.dispatch(saveMoneyAction) }}>赚钱</button> <hr /> {this.props.children} </div> ); } } class Child extends Component { componentDidMount() { store.subscribe(() => { this.setState({}) }) } render() { const state = store.getState(); return ( <div> <p>Family Money: {state.money}</p> Child: <button onClick={() => { const say = outMoney(20) store.dispatch(say) }}>索要零花钱</button> </div> ) } } export { Couple, Child };
javascript//action.js export const saveMoney = (money) => { return { type: 'SAVA_MONEY', payload: money } } export const outMoney = (money) => { return { type: 'OUT_MONEY', payload: money } }
三、react-redux
按照上面redux的使用流程,每个组件都需要通过store.getState获取state值,而且都需要订阅等等,真正项目中如果直接这么使用会代码冗余,不方便维护。
通过react-redux可以很好的简化上面的过程。
javascriptimport React, { Component } from 'react'; import { saveMoney, outMoney } from './store/action'; import { connect } from 'react-redux' class Couple extends Component { constructor(props) { super(props); this.state = {} } render() { const { money, saveMoney } = this.props; debugger return ( <div> <p>Family Money:{money}</p> Couple: <button onClick={() => { const saveMoneyAction = saveMoney(100) }}>赚钱</button> <hr /> {this.props.children} </div> ); } } class Child extends Component { render() { const { money, outMoney } = this.props; return ( <div> <p>Family Money: {money}</p> Child: <button onClick={() => { const say = outMoney(20) }}>索要零花钱</button> </div> ) } } //将store中的state数据映射到组件的props中 const mpStateToProps = (state) => { debugger return { money: state.money } } //将store中方法映射到组件到props中 const mpDisptchToProps = (dispatch) => { return { saveMoney: (money) => { dispatch(saveMoney(money)) }, outMoney: (money) => { dispatch(outMoney(money)) } } } const MyCouple = connect(mpStateToProps, mpDisptchToProps)(Couple); const MyChild = connect(mpStateToProps, mpDisptchToProps)(Child); export { MyCouple, MyChild };
项目入口 index.js
javascriptimport React from 'react'; import ReactDOM from 'react-dom'; import { Provider } from 'react-redux' import { MyCouple, MyChild } from './Family' import store from './store/store' const App = <Provider store={store}> <MyCouple> <MyChild /> <MyChild /> </MyCouple> </Provider> ReactDOM.render( App, document.getElementById('root') );
四、 Redux DevTools 插件
Chrome浏览器插件方便开发追溯redux状态的扭转记录。
javascript//创建store工厂 const store = createStore( reducer, initState, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__(), );
附加redux的三大原则(加深理解redux的好处和对付面试 ^ _ ^) (1)单一数据源:整个应用的state被存储在一棵object tree中,并且这个object tree只存在于唯一一个store中; (2)state是只读的:唯一改变state的方法就是触发action,action是一个用于描述发生事件的普通对象; (3)使用纯函数修改数据;