Redux 和 React-redux 使用指南
1、引入Redux
- createStore 方法
let store = createStore(todoApp, initialState, process.env.NODE_ENV === 'development' ? window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__() : null)
注意点:
1、由于React代码都要经过webpack处理的,所以在写React代码的时候,可以大致看成代码环境处于Node.js,所以上述的process.env 对象也就可以看成全局变量了,从而可以写区分环境的代码。
2、createStore第一个参数是reducer,即combineReducer的结果;第二个参数表示store的初始状态(可选);第三个参数表示是否启用Redux调试工具(通常是chrome的一个扩展工具)
3、store是一个对象,当传入initialState时,其初始值由initialState决定;不传时,由combineReducer的参数(也是一个对象,记为O)决定。需要注意的是对象store的key由O决定,而不是initialState决定,initialState只能传和O有相同属性的值
4、store的运转过程:1、dispatch(actionCreator(payload));2、reducer处理对应的action,从而修改store
5、connect函数的两个参数:mapStateToProps(state,ownProps) 和 mapDispatchToProps(dispatch,ownProps)
const mapStateToProps = state => {
return {
todos: getVisibleTodos(state.todos, state.visibilityFilter)
}
}
const mapDispatchToProps = dispatch => {
return {
onTodoClick: id => {
dispatch(toggleTodo(id))
}
}
}
const VisibleTodoList = connect(
mapStateToProps,
mapDispatchToProps
)(TodoList)
任何时候,只要 Redux store 发生改变,mapStateToProps 函数就会被调用。该回调函数必须返回一个纯对象,这个对象会与组件的 props 合并。如果你省略了mapStateToProps 这个参数,你的组件将不会监听 Redux store。如果指定了该回调函数中的第二个参数 ownProps,则该参数的值为传递到组件的 props,而且只要组件接收到新的 props,mapStateToProps 也会被调用(例如,当 props 接收到来自父组件一个小小的改动,那么你所使用的 ownProps 参数,mapStateToProps 都会被重新计算)。就是说不管store的变化是否是你关心的,你的mapStateToProps总是会被调用(即使这次调用是不必要的)
6、redux 如果不加中间件,则只能处理同步action
2、 Redux应用状态划分
- 应用状态扁平化
这里可以借助 normalizr 库,它可扁平化深层嵌套的json数据。比如下面这个博客数据:
{
"id": "123",
"author": {
"id": "1",
"name": "Paul"
},
"title": "My awesome blog post",
"comments": [
{
"id": "324",
"commenter": {
"id": "2",
"name": "Nicole"
}
},
{
"id": "325",
"commenter": {
"id": "3",
"name": "Tom"
}
}
]
}
可以扁平化成这样:
{
result: "123",
entities: {
"articles": {
"123": {
id: "123",
author: "1",
title: "My awesome blog post",
comments: [ "324" ]
}
},
"users": {
"1": { "id": "1", "name": "Paul" },
"2": { "id": "2", "name": "Nicole" }
},
"comments": {
"324": { id: "324", "commenter": "2" }
}
}
}
所经过的操作如下:
import { normalize, schema } from 'normalizr';
// Define a users schema
const user = new schema.Entity('users');
// Define your comments schema
const comment = new schema.Entity('comments', {
commenter: user
});
// Define your article
const article = new schema.Entity('articles', {
author: user,
comments: [comment]
});
const normalizedData = normalize(originalData, article);
- 抽离公共状态
待续。。 - 修改应用状态
1、Object.assign/Spread Operator
switch (action.type){
case SET_VISIBILITY_FILTER:
return {...state,visibilityFilter: action.filter}
default:
return state;
}
}
2、Immutable.js 或者 Seamless-Immutable.js
3、实现
- 简易版Redux(主要就是实现createStore,返回三个方法:getState,dispatch,subscribe。以及applyMiddleWare函数)
const createStore = reducer => {
let state = null;
const listeners = [];
const getState = () => state;
const subscribe = listener => listeners.push(listenter);
const dispatch = action =>{
state = reducer(state,action);
listeners.forEach(listenter => listenter());
};
dispatch({});
return {
getState,
dispatch,
subscribe
}
}
// 用法
const colorReducer = (state,action) => {
if(!state){
return {
color:red
}
}else{
switch(action.type){
case 'modifyColor':
return {
...state,
color:action.color
}
default:
return state;
}
}
}
const store = createStore(colorReducer);
store.dispatch({type:'modifyColor',color:'green'})
- applyMiddleware 实现
const applyMiddleWare = (...middlewares) => next =>{
return (reducer,initialState) =>{
let store = next(reducer,initialState);
let dispatch = store.dispatch;
let chain = [];
let middlewares = middlewares;
const middlewareAPI = {
getState: store.getState,
dispatch: action => dispatch(action)
}
chain = middlewares.map(value => value(middlewareAPI))
dispatch = compose(...chain )(store.dispatch)
return {
...store,
dispatch
}
}
}
function compose(...funcs) {
return arg => funcs.reduceRight((composed, f) => f(composed), arg);
}
- 简易版React-redux (主要就是实现connect高阶函数以及Provider组件)
const connect = (mapStateToProps,mapDispatchToProps) => WrappedComponent =>{
class Connect extends Component {
static contextType = {
store: PropTypes.object
}
constructor(){
this.state = {
allProps:{}
}
}
componentWillMount(){
this._updateProps();
this.context.store.subscribe(()=>this._updateProps()); // 订阅dispatch的变化,只要有dispatch,就会重新调用_updateProps,从而重新调用mapStateToProps和mapDispatchToProps触发React组件的生命周期,更新组件
}
_updateProps(){
const store = this.context.store;
const allProps = {};
const mapProps = mapStateToProps(store.getState(),this.props);
const mapDispatch = mapDispatchToProps(store.dispatch,this.props);
this.setState({
allProps: {
...this.props,
...mapProps,
...mapDispatch
}
})
}
render(){
return <WrappedComponent ...this.state.allProps />
}
}
return Connect
}
class Provider extends Component{
static propTypes = {
store: PropTypes.object,
children: PropTypes.any
}
static childContextType = {
store: PropTypes.object
}
getChildContext(){
return {
store:this.props.store || {}
}
}
render(){
return(
<div>this.props.children</div>
)
}
}
- 中间价的写法:
// 签名:
({dispatch, store}) => (next) => (action) => {}