注1:本文主要根据 "自述|redux中文文档" 学习的个人总结记录。也是是为了更快更好的学习和接受redux的操作,更好的应用的项目中。原文档地址:http://www.redux.org.cn/
注2:本文较长,可能会需要花一点时间去阅读和理解.
这篇文章主要介绍Redux中三个主要概念Action,Reducer,State。
1. Action
Action是我们事先定义好的一些对象,表示触发某个事件的标识。它必须有一个key为type字段,type的值类型为字符串,通常会单独建立一个文件存放所有的actionType,该文件定义的都是type的值.如下:
文件名:actionType.js export const FIRST_ACTION = 'FIRST_ACTION' export const MIDDLE_ACTION = 'MIDDLE_ACTION' ...
Action除了有个必须的type字段,我们还可以根据需要自定义一些其他的字段,这些字段承载着传入到store中,可以说是store中数据的唯一来源。 如下:
const DEMO_ACTION = 'DEMO_ACTION' //这里就不单独的建一个文件存actionType const action = { type: DEMO_ACTION,payload : '这里是可以传入到store中的数据' } store.dispatch(action) //通过store.dispatch将action传入到store
注意我们不会单独的为每次state的改变而去定义一个action,通常我们会声明一个action创建函数.
什么是action创建函数?其实就是生成action的方法,在redux中action创建函数只是简单的返回一个action:
function add() { return { type: 'ADD_WHAT',payload: {} } }
store 里能直接通过 store.dispatch() 调用 dispatch() 方法,但是多数情况下你会使用 react-redux 提供的 connect() 帮助器来调用。bindActionCreators() 可以自动把多个 action 创建函数 绑定到 dispatch() 方法上。关于bindActionCreators这个方法我们后面会介绍到。
2.Reducer
刚才我们介绍的action是用来描述发生某个特定的事情去触发state的改变,那么reducer就是定义如何去操作我们state的规则。
reducer是一个纯函数,它接受两个参数,第一个是旧的state,第二个是action对象,运行后返回一个state。
export function thisIsReducer(state={},action) { switch(action.type) { case 'XXXX': //... return Object.assign({},action.store) break case 'YYYY': //... return Object.assign({},action.store) break default: return state } }
注意一下:我们不要去修改state,所以看到上面是通过Object.assign新建了一个副本.还有不管怎么样都要返回一个state。
- 拆分reducer
在日常的开发中,一个应用不会只有一个reducer,通常我们会分为多个小的reducer,每一个reducer管理着state树中某一个state项,例如:
//主题列表reducer export function themeListStore(state={},actions) { switch (actions.type) { case ActionsType.THEME_LIST: state = Object.assign({},state,actions.store); state.datas.map((item,index)=>{ state.datas[index].isChecked = false; item.photoList.map((e,i)=>{ item.photoList[i].isChecked = 0; item.photoList[i].rotateAngle = item.photoList[i].userRotate; }) }); return state; case ActionsType.THEME_SORT: if( actions.dir == 'up' && actions.curId == 0 ) { return state }else if( actions.dir == 'down' && actions.curId == state.datas.length-1 ){ return state }else { if( actions.dir == 'up' ){ state.starId = state.datas[actions.curId].themeId state.endId = state.datas[actions.curId-1].themeId return state }else if( actions.dir == 'down' ){ state.starId = state.datas[actions.curId].themeId state.endId = state.datas[actions.curId+1].themeId return state } } default: return state } } //切换照片批量移动和主题编辑增加reducer export function changeOptStore(state=0,actions) { switch (actions.type) { case ActionsType.CHANGE_OPT: if(actions.option == 0){ state = 0 return state }else if(actions.option == 1){ state = 1 return state }else if(actions.option == 2){ state = 2 return state } console.log(state,'state') default: return state } }
上面的代码就是刚利用React-redux管理数据时写的reducer(有些不严谨的地方请忽略)。
注意每个 reducer 只负责管理全局 state 中它负责的一部分。每个 reducer 的 state 参数都不同,分别对应它管理的那部分 state 数据。
以上的只是一个文件中的reducer,一般更好的操作会根据不同的页面放到不同的文件中,保证其独立性,并用于处理不同的页面。
之前我们说过整个应用中只存在唯一的store,那么分散在不同文件中的reducer我们要如何整合在一起呢?
Redux提供了一个combineReducers()方法进行整合。
import {combineReducers} from 'redux'; import * as commonReducers from './commonReducers'; import * as alertMsgReducer from './alertMsgReducer'; import * as uploadPhotoReducer from './uploadPhotoReducer'; import * as photoGraphyReducer from './photoGraphyReducer'; const rootReducer = combineReducers({ ...commonReducers,...alertMsgReducer,...uploadPhotoReducer,...photoGraphyReducer }); export default rootReducer;
如上,我们通过import引入不同文件中所有的reducer,然后通过combineReducers进行整合一个根reduce人、,然后export出去。那么整合过后的是什么东西呢?我们打印出来看下:
如图,其生成的是一个函数,这个函数来调用你一系列的reducer,每个reducer根据它们的key来刷选出state中一部分数据并处理,然后这个生成的函数再将所有 reducer 的结果合并成一个大的对象。
3.Store
上面我们了解了action是预先定义好即将发生什么事件,reducer是定义对应事件处理数据的规则。那么store就是把他们联系到一起的对象:
- store是保存应用中所有的state, store是单一的
- store.getState()可以获取state
- store.dispatch(action) 触发action,改变state
- 通过 subscribe(listener) 注册监听器
- 通过 subscribe(listener) 返回的函数注销监听器
如何去创建一个store?
import {createStore} from 'redux' import rootReducerfrom './rootReducer' let store = createStore(rootReducer)
createStore还有第二个参数,用来设置state的初始状态,那么这样对于同构应用可以进行服务端渲染(文档原话:这对开发同构应用时非常有用,服务器端 redux 应用的 state 结构可以与客户端保持一致,那么客户端可以将从网络接收到的服务端 state 直接用于本地数据初始化。)
let store = createStore(todoApp,window.STATE_FROM_SERVER)
4.数据流
Redux中是严格的单项数据流。Redux中的从改变到渲染到页面中主要有四个步骤:
- 在view层通过事件dispatch某个action(action是描述发生某个事情的对象)
- Store调用传入的reducer函数,之前说过reducer会接受两个参数(之前的state和action对象)
- 根reducer会把多个子reducer合并输出成一个单一的state树
- Redux store 保存了根 reducer 返回的完整 state 树。
这个新的树就是应用的下一个 state!所有订阅 store.subscribe(listener) 的监听器都将被调用;监听器里可以调用 store.getState() 获得当前 state。
看上面,又找到一个感觉比较好的图可以表示我们的数据流向。这里就不重复的解释了,对照之前说的我相信可以看得清晰又明白~
总结
这里简单归纳一下:action是我们根据需要定义好一些action对象,我们通过store.dispatch(action)会触发队一行的reducer函数,reducer根据规则改变state,最后改变后的数据会呈现在我们的视图上。后面我们会继续介绍react中使用react-redux,以及异步action的操作等..