如果React只是入门的同学,或者对Redux这个状态管理库不是很了解的朋友,这篇文章应该会对你有些帮助。
了解 React 的朋友应该都知道React是状态数据驱动的框架。React组件组件之间传值有很多种情况,但是组件之间传值只有通过props,context,回调函数 来实现(不借助外部类库)。
上面是一些最常用的情况,但是如果在大型项目中,组件嵌套四五层层甚至更多,这时候有个需求说把第4层的一个值传给另一个关系甚远的堂兄组件,怎么搞?如果一层层的向上传值找到公共的祖先组件然后再通过props一层层的传?
这个时候如果有一个仓库能够帮我们管理这些状态,组件直接的修改值,获取值直接通过这个仓库来统一管理,再也不用像上面一层层的传值,是不是很爽?
根据redux的工作流程画了个简单的流程图。接下来我们根据流程图来说下redux数据流是怎么流转的。
首先我们需要创建一个仓库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发送最新的数据。
接下来我将写代码模拟父母挣钱,孩子索要零花钱来改变家庭资金的过程。
create-react-app redux-demo
//脚手架安装 npm install -g create-react-app
//store.js
import { createStore} from 'redux';
import reducer from './reducer'
//初始化工厂数据 state
const initState = { money: 0 };
//创建store工厂
const store = createStore(
reducer,
initState,
);
export default store;
//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;
//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 };
//action.js
export const saveMoney = (money) => {
return {
type: 'SAVA_MONEY',
payload: money
}
}
export const outMoney = (money) => {
return {
type: 'OUT_MONEY',
payload: money
}
}
按照上面redux的使用流程,每个组件都需要通过store.getState获取state值,而且都需要订阅等等,真正项目中如果直接这么使用会代码冗余,不方便维护。
通过react-redux可以很好的简化上面的过程。
import 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
import 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')
);
Chrome浏览器插件方便开发追溯redux状态的扭转记录。
//创建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)使用纯函数修改数据;