首先,redux是什么?为什么我们要用redux呢?
随着应用越来越大,我们会发现会有很多数据需要管理。如果一个 model 的变化会引起另一个 model 变化,那么当 view 变化时,就可能引起对应 model 以及另一个 model 的变化,依次地,可能会引起另一个 view 的变化。直至你搞不清楚到底发生了什么。
当然如果你的应用使用方式很简单,没有太多复杂数据交互的时候,你也可以不用
毕竟子曾经曰过:
"如果你不知道是否需要 Redux,那就是不需要它。"
"只有遇到 React 实在解决不了的问题,你才需要 Redux 。"
如图,是redux的简单流程
--> 用户对界面进行一些操作触发action
--> action经过一些中间件被传到reducer中
--> reducer对action和preState作出一些*操作,并返回一个新的state
--> 将新的state传给界面展示出来
简单解释一下这些是什么
action: 是一个对象,还是个有type的对象,描述我要干啥。比如:{ type:‘买碗热干面’,money:"1.5" }
middleware: 中间件,内部*操作(后议)。比如你给的是美元,我给你换成人民币
state: 某一时间点的数据集合。比如要买热干面肯定要有钱吧,现在有五块钱,这就是我现在的state
reducer: 纯函数,在当前state执行action,返回新的state。比如你给我五块钱去买热干面,花了一块五,还剩三块五,返回的新的state的钱就只有三块五了
store: 保存所有数据的地方(可能这就是大佬吧)
从代码上看,如下是redux提供的几个主要函数:
首先通过 createStore 创建 store
store = createStore(reducer, preloadedState, enhancer)
store 这个对象还提供了跟多方法供外接调用,例如:
store.subscribe(listener)
监听 Redux 中 state 的变化,一旦Redux中的state发生了变化,render函数就会被调用,页面就会被重新渲染
store.dispatch(action)
更新状态,分发action和当前状态到reducer,reducer会返回一个新的状态
state = store.getState()
获取当前状态
rootReducer = combineReducers(reducers)
reducer可以根据功能拆分成很多个reducer,通过combineReducer将多个reducer组合成一个
bindActionCreators(actionCreators, dispatch)
绑定action和dispatch 相当于dispatch(action)
那
.
.
.
// 计数器,可以进行加减计数
const Counter = ({ value, onIncrement, onDecrement }) => (
<div>
<h1>{value}</h1>
<button onClick={onIncrement}>+</button>
<button onClick={onDecrement}>-</button>
</div>
);
// reducer,默认state为0,接收到INCREMENT时,数字+1,DECREMENT数字-1
const reducer = (state = 0, action) => {
switch (action.type) {
case 'INCREMENT': return state + 1;
case 'DECREMENT': return state - 1;
default: return state;
}
};
// 创建store
const store = createStore(reducer);
const render = () => {
ReactDOM.render(
<Counter
value={store.getState()} //获取当前状态(0)作为初值
onIncrement={() => store.dispatch({type: 'INCREMENT'})} //点击按钮时dispatch action
onDecrement={() => store.dispatch({type: 'DECREMENT'})}
/>,
document.getElementById('root')
);
};
render();
store.subscribe(render); // 监听state的变化并重新渲染界面
这是两条分割线
知道了 redux 那怎么优雅的结合react和redux呢
于是......
就有了react-redux
react-redux源码比较复杂,就不一一分析了,我知道你们也不想看(我也没太细看)
直接拿栗子讲吧
以 todomvc 为例,要添加一个代办事项,经历了什么
以下代码见 feature/reduxTodo 分支
目录结构(按道理应该是介个样子滴,但是我没完全按这个写):
- actions :放置action
- components : UI组件,只负责UI呈现,没有交互,数据全由props传入
- container : 容器组件,处理业务逻辑,管理数据
- reducer : 放置reducer
- index.js : 顶层组件
const ADD_TODO = 'ADD_TODO';
let nextTodoId = 0;
export const addTodo = text => ({
type: ADD_TODO, // 一般用字符串常量作为type,防止字符串会出现重复
id: nextTodoId++,
completed: false
text
});
包含很多 action,导出的是对象形式,函数名即为键名。
以 addTodo 为例,该对象需要 type、id、completed(是否已完成的状态) 和 text(todo事项描述)
const todos = (state = [], action) => {
switch (action.type) {
case ADD_TODO:
return [
...state,
{
id: action.id,
text: action.text,
completed: false
}
];
}
输入当前 state 和 action ,根据 action.type 区分不同 action,并对 state 进行一些操作后返回新的 state 。对 addTodo 即为将新的对象加入到之前的对象数组中。
handler() {
const {
todoActions: { addTodo }
} = this.props;
!!this.input.current.value && addTodo && addTodo(this.input.current.value);
this.input.current.value = '';
}
......
<input
className="todo-new"
placeholder="what needs to be done?"
ref={this.input}
type="text"
onBlur={() => this.handler()}
onKeyUp={e => this.onKeyup(e)}
/>
......
// 把state传给props
const mapStateToProps = state => ({
todoList: state.todos
});
// 将action绑定到dispatch
const mapDispatchToProps = dispatch => ({
todoActions: bindActionCreators(todoActions, dispatch)
});
module.exports = connect(
mapStateToProps,
mapDispatchToProps
)(App);
这里比较重要的是 connect() 方法
connect(mapStateToProps, mapDispatchToProps)(MyComponent) connet 可接受四个参数,这里主要介绍前两个。
mapStateToProps:负责输入逻辑,将 state 映射到组件参数props中。接受 state 作为参数,返回一个对象,里面每一个键值对就是一个映射。当然你也可以对 state.todos
进行一些操作再传给 todoList(个人不建议了,感觉很乱)
mapDispathcToProps:负责输出逻辑,将用户对UI的操作映射成Action。接受dispatch和ownProps(容器组件props对象)作为参数,返回的也是个对象。键值是一个函数用于dispatch action。
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import App from './containers/App';
import store from './store/store';
import rootReducer from '../reducers';
const store = createStore(rootReducer);
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
因为 store 有且只有一个,而我们需要将 redux 中的数据传给 react 使用,所以需要在顶层将 store 传入,再一层一层往下传给组件。
而react-redux提供了<provider>
组件,让容器组件可以通过 connect 方法拿到 state
redux的中间件就有很多了,常见的比如
redux-thunk — 用最简单的方式搭建异步 action 构造器,有了这个就可以在action里写异步函数了
redux-promise — 遵从 FSA 标准的 promise 中间件
redux-logger — 记录所有 Redux action 和下一次 state 的日志
redux-devtools-extension - 浏览器中可以查看redux的变化
时间原因就不细说了
可能有下回分解