redux核心思路和代码解析
最近在公司内部培训的时候,发现很多小伙伴只是会用redux、react-redux、redux-thunk的api,对于其中的实现原理和数据真正的流向不是特别的清楚,知其然,也要知其所以然,其实redux的源代码非常简介,下面逐一介绍,
1.先看一个简单的redux应用的例子:
import { createStore, combineReducers } from 'redux'; const year = (state, action) => {
let defaultState = {
year: 2017
}
state = state || defaultState;
switch (action.type) {
case 'add':
return {
year: state.year + 1
};
break;
case 'sub':
return {
year: state.year - 1
};
break;
default:
return state;
break;
}
} const count = (state, action) => {
let defaultState = {
count: 1
}
state = state || defaultState;
switch (action.type) {
case 'addone':
return {
count: state.count + 1
};
break;
case 'subone':
return {
count: state.count - 1
};
break;
default:
return state;
break;
}
} const reducer = combineReducers({
year: year,
count: count
}) const store = createStore(reducer); store.subscribe(() => {
console.log('the year is ' + store.getState().year.year);
});
store.subscribe(() => {
console.log('数字是' + store.getState().count.count);
}); const action1 = {
type: 'add'
}
const action2 = {
type: 'addone'
}
const action3 = {
type: 'hello'
} store.dispatch(action1);
store.dispatch(action2);
store.dispatch(action3);
上面代码执行后打印出来的结果是:
想必大部分小伙伴都觉得很简单。
export default function createStore(reducer, preloadedState, enhancer) {
function getState() {}
function subscribe(listener) {}
function dispatch(action) {}
function replaceReducer(nextReducer) {}
...
return {
dispatch,
subscribe,
getState,
replaceReducer,
...
} }
2.1.1 subscribe方法的核心代码,就是把一个一个的监听函数放入到Listeners的数组,然后返回一个unsubscribe函数,一个闭包函数,包含着传入的listeners函数,执行该函数从监听数组中移除该listeners
function subscribe(listener) {
if (typeof listener !== 'function') {
throw new Error('Expected listener to be a function.')
} let isSubscribed = true ensureCanMutateNextListeners()
nextListeners.push(listener) return function unsubscribe() {
if (!isSubscribed) {
return
} isSubscribed = false ensureCanMutateNextListeners()
const index = nextListeners.indexOf(listener)
nextListeners.splice(index, 1)
}
}
2.1.2 getState 直接返回当前的state的值,如果没有进行任何操作,直接返回默认值,如果是经过dispatch(action)之后,在dispatch中触发了reducer函数,生成了新的state,也是直接返回
function getState() {
return currentState
}
2.1.3 dispatch store上的最核心方法, 两部分组成,第一部分直接把当前的state和传入的action直接传入reducer函数,执行,生成新的state,供getState使用,第二部分是直接循环执行subscribe中添加到listeners数组中的监听函数,也就是触发监听函数,
逻辑非常简单。
function dispatch(action) {
try {
isDispatching = true
currentState = currentReducer(currentState, action)
} finally {
isDispatching = false
} const listeners = currentListeners = nextListeners
for (let i = 0; i < listeners.length; i++) {
const listener = listeners[i]
listener()
} return action
}
2.2 combineReducers,将多个reducer合并成一个rootReducer,本质上为合并对象,并返回一个新的总的的reducer函数
function combineReducers(reducers) {
const reducerKeys = Object.keys(reducers)
const finalReducers = {}
for (let i = 0; i < reducerKeys.length; i++) {
const key = reducerKeys[i]
if (typeof reducers[key] === 'function') {
finalReducers[key] = reducers[key]
}
}
const finalReducerKeys = Object.keys(finalReducers)
return function combination(state = {}, action) {
let hasChanged = false
const nextState = {}
for (let i = 0; i < finalReducerKeys.length; i++) {
const key = finalReducerKeys[i]
const reducer = finalReducers[key]
const previousStateForKey = state[key]
const nextStateForKey = reducer(previousStateForKey, action)
if (typeof nextStateForKey === 'undefined') {
const errorMessage = getUndefinedStateErrorMessage(key, action)
throw new Error(errorMessage)
}
nextState[key] = nextStateForKey
hasChanged = hasChanged || nextStateForKey !== previousStateForKey
}
return hasChanged ? nextState : state
}
}
combineReducers的一个简单实现方式,更能明白其中的工作原理:
const combineReducers = (reducers) => {
return (state = {}, action) => {
return Object.keys(reducers).reduce(
(nextState, key) => {
nextState[key] = reducers[key](
state[key],
action);
return nextState;
},
{})
}
}
2.3 applyMiddleware 中间件,对dispatch的增强,一般为添加异步操作等,例如redux-thunk中间件,实际上就是如果action是个方法,则执行这个方法,如果不是则正常dispatch(action)
function applyMiddleware(...middlewares) {
return (createStore) => (reducer, preloadedState, enhancer) => {
const store = createStore(reducer, preloadedState, enhancer)
let dispatch = store.dispatch
let chain = [] const middlewareAPI = {
getState: store.getState,
dispatch: (action) => dispatch(action)
}
chain = middlewares.map(middleware => middleware(middlewareAPI))
dispatch = compose(...chain)(store.dispatch) return {
...store,
dispatch
}
}
} function createThunkMiddleware(extraArgument) {
return ({ dispatch, getState }) => next => action => {
if (typeof action === 'function') {
return action(dispatch, getState, extraArgument);
} return next(action);
};
} const thunk = createThunkMiddleware();
thunk.withExtraArgument = createThunkMiddleware; export default thunk;
3.react-redux
3.1通过provider 提供store,react通过Context属性,可以将props直接给子孙component,无须通过props层层传递,Provider仅仅起到获得store, 然后将其传递给子孙元素而已
3.2 connect 这个是最关键的方法,
redux核心思路和代码解析的更多相关文章
- 学习Redux之分析Redux核心代码分析
1. React,Flux简单介绍 学习React我们知道,React自带View和Controller库,因此,实现过程中不需要其他任何库,也可以独立开发应用.但是,随着应用程序规模的增大,其需要控 ...
- java集合框架之java HashMap代码解析
java集合框架之java HashMap代码解析 文章Java集合框架综述后,具体集合类的代码,首先以既熟悉又陌生的HashMap开始. 源自http://www.codeceo.com/arti ...
- Java面试 32个核心必考点完全解析
目录 课程预习 1.1 课程内容分为三个模块 1.2 换工作面临问题 1.3 课程特色 课时1:技术人职业发展路径 1.1 工程师发展路径 1.2 常见技术岗位划分 1.3 面试岗位选择 1.4 常见 ...
- [代码]解析nodejs的require,吃豆人的故事
最近在项目中需要对nodejs的require关键字做解析,并且替换require里的路径.一开始我希望nodejs既然作为脚本语言,内核提供一个官方的parser库应该是一个稳定可靠又灵活的渠道,然 ...
- Beam Search快速理解及代码解析(上)
Beam Search 简单介绍一下在文本生成任务中常用的解码策略Beam Search(集束搜索). 生成式任务相比普通的分类.tagging等NLP任务会复杂不少.在生成的时候,模型的输出是一个时 ...
- Beam Search快速理解及代码解析
目录 Beam Search快速理解及代码解析(上) Beam Search 贪心搜索 Beam Search Beam Search代码解析 准备初始输入 序列扩展 准备输出 总结 Beam Sea ...
- [nRF51822] 12、基础实验代码解析大全 · 实验19 - PWM
一.PWM概述: PWM(Pulse Width Modulation):脉冲宽度调制技术,通过对一系列脉冲的宽度进行调制,来等效地获得所需要波形. PWM 的几个基本概念: 1) 占空比:占空比是指 ...
- MYSQL常见出错mysql_errno()代码解析
如题,今天遇到怎么一个问题, 在理论上代码是不会有问题的,但是还是报了如上的错误,把sql打印出來放到DB中却可以正常执行.真是郁闷,在百度里面 渡 了很久没有相关的解释,到时找到几个没有人回复的 & ...
- 【原创】大数据基础之Spark(5)Shuffle实现原理及代码解析
一 简介 Shuffle,简而言之,就是对数据进行重新分区,其中会涉及大量的网络io和磁盘io,为什么需要shuffle,以词频统计reduceByKey过程为例, serverA:partition ...
随机推荐
- 初识PHP遗留下来的问题?
待解决的问题: 1.写一个PHP脚本,显示用户输入的名称. 提示: <?php echo $_POST["username"];?>! <?php echo &q ...
- maven 配置安装
1.下载maven http://maven.apache.org/ 2.windows安装maven 解压包后配置环境变量 PATH:%M2_HOME%\bin M2_HOME:D:\soft\ ...
- 矢量切片(Vector tile)
说明:本月的主要工作都是围绕制作矢量切片这一个核心问题进行的,所以2月的主题就以这个问题为主,目前分支出来的一些内容主要包括了TMS(Tile map service),OpenLayers3中的Pr ...
- wcf ServiceContract
ServiceContract是什么 ServiceContract怎么用
- Jenkins添加用户
新建用户 Jenkins刚开始的界面是允许访客进行所有操作的,这时Jenkins是有安全隐患的,也不容易去管理.这时,我们需要管理Jenkins的权限,对它的权限进行设置.关于Jenkins权限设置的 ...
- 用《内网穿山甲》把本地IIS中的站点共享到远程访问
前言: 因为各种原因,我们常常要把本机或局域网中搭建的站点发给远方的人访问,他有可能是测试人员.客户.前端.或领导演示,或是内部系统内部论坛临时需要在远程访问,事件变得很麻烦,要么有公网IP,要么能控 ...
- JAVA函数的参数传递
JAVA开发过程中写函数都是不可避免的于是乎参数传递经常会困扰我们,特别是有C/C++功底的童鞋总会纠结于"java到底是值传递还是引用传递?" 先来一段代码(和程序员交流最好的果 ...
- DLL 导出变量
声明为导出变量时,同样有两种方法: 第一种是用__declspec进行导出声明 #ifndef _DLL_SAMPLE_H #define _DLL_SAMPLE_H // 如果定义了C++编译器 ...
- 虚拟机Centos开机以后,有eth0网卡,但是没有IP,Determine IP information for eth0.. no link present check cable
Determine IP information for eth0.. no link present check cable 如果你的VMware虚拟机centos6.5使用NAT模式,开机以后,使 ...
- 【转】svn cleanup failed–previous operation has not finished; run cleanup if it was interrupted
svn提交遇到恶心的问题,可能是因为上次cleanup中断后,进入死循环了. 错误如下 解决方法:清空svn的队列 1.下载sqlite3.exe 2.找到你项目的.svn文件,查看是否存在wc.db ...