blog
blog copied to clipboard
Redux入门
介绍
Redux 是 JavaScript 状态容器, 试图让 state 的变化变得可预测。
使用场景
- 用户的使用方式复杂
- 不同身份的用户有不同的使用方式(比如普通用户和管理员)
- 多个用户之间可以协作
- 与服务器大量交互,或者使用了 WebSocket
- View 要从多个来源获取数据
flux 架构
flux 的单向数据流图
/*
_________ ____________ ___________
| | | | | |
| Action |------------▶| Dispatcher |------------▶| callbacks |
|_________| |____________| |___________|
▲ |
| |
| |
_________ ____|_____ ____▼____
| |◀----| Action | | |
| Web API | | Creators | | Store |
|_________|----▶|__________| |_________|
▲ |
| |
____|________ ____________ ____▼____
| User | | React | | Change |
| interactions |◀--------| Views |◀-------------| events |
|______________| |___________| |_________|
*/
结合 Redux 分析流程:
ActionCreator -> Action -> dispatcher -> middleware -> reducers -> Store -> Change events -> React Views
核心概念
action: 描述行为的指示器,是一个用于描述已发生事件的普通对象。
state: 应用的全局状态,唯一改变 state 的方法就是触发 action。
reducer:state 函数更新规则,接收 state 和 action,并返回新的 state
Demo
actions
function increment(num) {
return {
type: 'INCREMENT',
num
};
}
function decrement(num) {
return {
type: 'DECREMENT',
num
};
}
reducers
var initCount = {
count: 0
};
function counter(state, action) {
if (!state) {
return initCount;
}
switch (action.type) {
case 'INCREMENT':
return { count: state.count + Number(action.num) };
case 'DECREMENT':
return { count: state.count - Number(action.num) };
default:
return state;
}
}
不要修改 state,为 action 返回一个新的 state; 在遇到未知 action 时,默认情况下一定要返回旧的 state
store
store 是一个对象,使用 Redux 提供的createStore
方法来生成,我们需要将 Reducer 作为参数传进去,在本例中:
var store = Redux.createStore(counter);
store 拥有以下方法
- 通过
store.getState()
f 方法 获取 state; - 通过
store.dispatch(action)
方法来更新 state; - 通过
store.subscribe(listener)
方法来注册监听器,state 变化时自动执行该函数;
通过 store.getState()
获取 state,并根据 state 值来设置 span 的初始值
function renderValue() {
document.querySelector('span').innerHTML = store.getState().count;
}
// 首次执行
renderValue();
注册监听器,每当 state 发生变化时执行上面的渲染函数
store.subscribe(renderValue);
整合逻辑,触发 action
最后通过 store.dispatch(action)
来触发修改 state 的操作,写在事件处理程序中,点击按钮时修改 state:
document.querySelector('.increment').onclick = function() {
let num = document.querySelector('input').value;
setTimeout(() => {
store.dispatch(increment(num));
}, 0);
};
document.querySelector('.decrement').onclick = function() {
let num = document.querySelector('input').value;
store.dispatch(decrement(num));
};
middleware
中间件,redux 中createStore
的第三个参数支持添加中间件。
import { createStore, applyMiddleware } from 'redux';
import logger from 'redux-logger';
const error = store => next => action => {
try {
next(action);
} catch (e) {
console.log('error', e);
}
};
const store = createStore(rootReducer, {}, applyMiddleware(logger, error));
上图中的logger
中间件可以自己书写:
const logger = store => next => action => {
console.log('dispatching', action);
let result = next(action);
console.log('next state', store.getState());
return result;
};
const logger = function(store) {
return function(next) {
return function(action) {
console.log('dispatching...', action);
let result = next(action);
console.log('next State', store.getState());
return result;
};
};
};
react-redux
react-redux 的作用是将 redux 的的反应更好的流动(绑定到)reac 应用上。
<Provider store>
作用在于将由 redux 创建的 store 传递到内部组件中,内部组件可以使用这个 store 并提供对 state 的更新。
所以说Provider
可以只在 react 应用最外包裹层注入 store 就可以了。
ReactDOM.render(
<Provider store={store}>
<Router history={history}>
<Route path="/" component={App}>
<Route path="foo" component={Foo}/>
<Route path="bar" component={Bar}/>
</Route>
</Router>
</Provider>,
document.getElementById('root')
connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options])
connect()一共有四个参数,常用的有两个mapStateToProps
和mapDispatchToProps
- mapStateToProps:把状态绑定到组件的属性当中。我们定义的 state 对象有哪些属性,在我们组件的 props 都可以查阅和获取。
const mapStateToProps = state => {
return {
counter: state.counter
};
};
state 相当于 store.getState() 通过 connect 绑定到组件的 props 上进行获取,返回的对象键可以自定义;
- mapDispatchToProps:在 redux 中介绍过,用 store.dispatch(action)来发出操作,那么我们同样可以把这个方法封装起来,即绑定到我们的方法中。
import { increment, decrement } from './actions'; // 少量action
const mapDispatchToProps = dispatch => {
return {
increment: () => dispatch(increment()),
decrement: () => dispatch(decrement())
};
};
补充
单个声明式导出 action 方法和多个一块导出,这种 props 数据比较清晰
import { increment, decrement } from './actions'; // 少量action
// import * as types from './actions'; // 多个action
另一种方法借用bindActionCreators
api,这样会将所有的 action 一同绑定,适情况使用。
import * as types from './actions'; // 多个action
const mapDispatchToProps = dispatch => {
return bindActionCreators(types, dispatch);
};
最后 connect()将 state 和 dispatch 都绑定到导出的组件上,redux 数据就经过 react-redux 流动到了 react 组件上。
export default connect(
mapStateToProps,
mapDispatchToProps
)(App);
Redux DevTools
参考
- 官方文档
- redux-tutorial-cn
- https://www.jianshu.com/p/1a2f3db4af61
- https://www.jianshu.com/p/3334467e4b32
- https://github.com/evgenyrodionov/redux-logger
- https://github.com/gaearon/redux-thunk
- https://github.com/pburtchaell/redux-promise-middleware
- https://github.com/zalmoxisus/redux-devtools-extension