经过了这么多节的优化,我们有了一个很通用的 createStore

function createStore (state, stateChanger) {
const listeners = []
const subscribe = (listener) => listeners.push(listener)
const getState = () => state
const dispatch = (action) => {
state = stateChanger(state, action) // 覆盖原对象
listeners.forEach((listener) => listener())
}
return { getState, dispatch, subscribe }
}

它的使用方式是:

let appState = {
title: {
text: 'React.js 小书',
color: 'red',
},
content: {
text: 'React.js 小书内容',
color: 'blue'
}
} function stateChanger (state, action) {
switch (action.type) {
case 'UPDATE_TITLE_TEXT':
return {
...state,
title: {
...state.title,
text: action.text
}
}
case 'UPDATE_TITLE_COLOR':
return {
...state,
title: {
...state.title,
color: action.color
}
}
default:
return state
}
} const store = createStore(appState, stateChanger)
...

我们再优化一下,其实 appState 和 stateChanger 可以合并到一起去:

function stateChanger (state, action) {
if (!state) {
return {
title: {
text: 'React.js 小书',
color: 'red',
},
content: {
text: 'React.js 小书内容',
color: 'blue'
}
}
}
switch (action.type) {
case 'UPDATE_TITLE_TEXT':
return {
...state,
title: {
...state.title,
text: action.text
}
}
case 'UPDATE_TITLE_COLOR':
return {
...state,
title: {
...state.title,
color: action.color
}
}
default:
return state
}
}

stateChanger 现在既充当了获取初始化数据的功能,也充当了生成更新数据的功能。如果有传入 state 就生成更新数据,否则就是初始化数据。这样我们可以优化 createStore 成一个参数,因为 state 和 stateChanger 合并到一起了:

function createStore (stateChanger) {
let state = null
const listeners = []
const subscribe = (listener) => listeners.push(listener)
const getState = () => state
const dispatch = (action) => {
state = stateChanger(state, action)
listeners.forEach((listener) => listener())
}
dispatch({}) // 初始化 state
return { getState, dispatch, subscribe }
}

createStore 内部的 state 不再通过参数传入,而是一个局部变量 let state = nullcreateStore 的最后会手动调用一次 dispatch({})dispatch 内部会调用 stateChanger,这时候的 state 是 null,所以这次的 dispatch 其实就是初始化数据了。createStore 内部第一次的 dispatch 导致 state 初始化完成,后续外部的 dispatch 就是修改数据的行为了。

我们给 stateChanger 这个玩意起一个通用的名字:reducer,不要问为什么,它就是个名字而已,修改 createStore 的参数名字:

function createStore (reducer) {
let state = null
const listeners = []
const subscribe = (listener) => listeners.push(listener)
const getState = () => state
const dispatch = (action) => {
state = reducer(state, action)
listeners.forEach((listener) => listener())
}
dispatch({}) // 初始化 state
return { getState, dispatch, subscribe }
}

这是一个最终形态的 createStore,它接受的参数叫 reducerreducer 是一个函数,细心的朋友会发现,它其实是一个纯函数(Pure Function)。

reducer

createStore 接受一个叫 reducer 的函数作为参数,这个函数规定是一个纯函数,它接受两个参数,一个是 state,一个是 action

如果没有传入 state 或者 state 是 null,那么它就会返回一个初始化的数据。如果有传入 state 的话,就会根据 action 来“修改“数据,但其实它没有、也规定不能修改 state,而是要通过上节所说的把修改路径的对象都复制一遍,然后产生一个新的对象返回。如果它不能识别你的 action,它就不会产生新的数据,而是(在 default 内部)把 state 原封不动地返回。

reducer 是不允许有副作用的。你不能在里面操作 DOM,也不能发 Ajax 请求,更不能直接修改 state,它要做的仅仅是 —— 初始化和计算新的 state

现在我们可以用这个 createStore 来构建不同的 store 了,只要给它传入符合上述的定义的 reducer 即可:

function themeReducer (state, action) {
if (!state) return {
themeName: 'Red Theme',
themeColor: 'red'
}
switch (action.type) {
case 'UPATE_THEME_NAME':
return { ...state, themeName: action.themeName }
case 'UPATE_THEME_COLOR':
return { ...state, themeColor: action.themeColor }
default:
return state
}
} const store = createStore(themeReducer)
...

下一节:动手实现 Redux(六):Redux 总结

上一节:动手实现 Redux(四):共享结构的对象提高性能

动手实现 Redux(五):不要问为什么的 reducer的更多相关文章

  1. 动手实现 Redux(六):Redux 总结

    不知不觉地,到这里大家不仅仅已经掌握了 Redux,而且还自己动手写了一个 Redux.我们从一个非常原始的代码开始,不停地在发现问题.解决问题.优化代码的过程中进行推演,最后把 Redux 模式自己 ...

  2. 动手实现 Redux(四):共享结构的对象提高性能

    接下来两节某些地方可能会稍微有一点点抽象,但是我会尽可能用简单的方式进行讲解.如果你觉得理解起来有点困难,可以把这几节多读多理解几遍,其实我们一路走来都是符合“逻辑”的,都是发现问题.思考问题.优化代 ...

  3. 动手实现 Redux(三):纯函数(Pure Function)简介

    我们接下来会继续优化我们的 createStore 的模式,让它使我们的应用程序获得更好的性能. 但在开始之前,我们先用一节的课程来介绍一下一个函数式编程里面非常重要的概念 —— 纯函数(Pure F ...

  4. 动手实现 Redux(二):抽离 store 和监控数据变化

    上一节 的我们有了 appState 和 dispatch: let appState = { title: { text: 'React.js 小书', color: 'red', }, conte ...

  5. 动手实现 Redux(一):优雅地修改共享状态

    从这节起我们开始学习 Redux,一种新型的前端“架构模式”.经常和 React.js 一并提出,你要用 React.js 基本都要伴随着 Redux 和 React.js 结合的库 React-re ...

  6. C语言经典算法五个人问岁数!——————【Badboy】

    有5 个人坐在一起,问第五个人多少岁?他说比第4 个人大2 岁.问第4 个人岁数.他说比第3 个人大2 岁.问第三个人,又说比第2 人大两岁.问第2 个人.说比第一个人大两岁.最后问第一个人.他说是1 ...

  7. 设计模式之十五:訪问者模式(Visitor Pattern)

    訪问者模式(Visitor Pattern)是GoF提出的23种设计模式中的一种,属于行为模式. 据<大话设计模式>中说算是最复杂也是最难以理解的一种模式了. 定义(源于GoF<De ...

  8. 面渣逆袭:JVM经典五十问,这下面试稳了!

    大家好,我是老三,"面渣逆袭"系列继续,这节我们来搞定JVM.说真的,JVM调优什么的一个程序员可能整个职业生涯都碰不到两次,但是,一旦用到的时候,那就是救命了,而且最重要的是-- ...

  9. 动手学servlet(五) 共享变量

    1. 无论对象的作用域如何,设置和读取共享变量的方法是一致的 -setAttribute("varName",obj); -getAttribute("varName&q ...

随机推荐

  1. andfix使用

    1.andfix简介 AndFix是一个Android App的在线热补丁框架.使用此框架,我们能够在不重复发版的情况下,在线修改App中的Bug.AndFix就是 “Android Hot-Fix” ...

  2. JS中使用组合构造函数模式和原型模式

    创建自定义类型的最常见方式,就是组合使用构造函数模式与原型模式.构造函数模式用于定义实例属性,而原型模式用于定义方法和共享的属性. 结果,每个实例都会有自己的一份实例属性的副本,但同时又共享着对方法的 ...

  3. CentOS 7.2安装Jenkins自动构建Git项目

    1.环境 本文使用VMWare虚拟机进行实验. 最终实现目标,在Jenkins服务器上新建构建任务,从Git服务器上拉取master HEAD(不编译,仅演示),部署到"目标服务器" ...

  4. My Notes

    1.类似于border.margin.padding的四个方向数值顺序为上右下左.2.属性z-index参数值越大,则被层叠在最上面.3.标签<a>和属性display:block和适合在 ...

  5. Intel® Media SDK Media Samples Linux 学习笔记(转)

    最近折腾intel media sdk,主要硬件平台是在HD4600的核显上进行测试,intel media sdk是intel提供的一种基于核显的硬件编解码的解决方案,之前已经有使用ffmpeg进行 ...

  6. [SoapUI] Reference parameter 引用变量

    Reference parameter in WADL : Endpoint : ${#Project#DomainServer} Resource : {AdvisorID} Reference p ...

  7. 【USACO】 奶牛政坛

    [题目链接] 点击打开链接 [算法] tarjan算法求LCA [代码] #include<bits/stdc++.h> #define MAXN 200010 #pragma GOC o ...

  8. JAVA interrupte中断线程的真正用途

    Java线程之中,一个线程的生命周期分为:初始.就绪.运行.阻塞以及结束.当然,其中也可以有四种状态,初始.就绪.运行以及结束. 一般而言,可能有三种原因引起阻塞:等待阻塞.同步阻塞以及其他阻塞(睡眠 ...

  9. ML一些零散记录

    朴素贝叶斯的假定条件:变量独立同分布 一般情况下,越复杂的系统,过拟合的可能性就越高,一般模型相对简单的话泛化能力会更好一点,增加隐层数可以降低网络误差(也有文献认为不一定能有效降低),提高精度,但也 ...

  10. python整数与IP地址转换 [转]

    我们有时会将一个整数与IP地址进行互换,用python代码实现很简单将一个整数如2000000,变为一个IP地址的方式 >>> import socket >>> ...