经过了这么多节的优化,我们有了一个很通用的 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. 利用CoreTelephony获取用户当前网络状态(判断2G,3G,4G)

    前言: 在项目开发当中,往往需要利用网络.而用户的网络环境也需要我们开发者去注意,根据不同的网络状态作相应的优化,以提升用户体验. 但通常我们只会判断用户是在WIFI还是移动数据,而实际上,移动数据也 ...

  2. Codeforces Beta Round #25 (Div. 2 Only) C. Roads in Berland

    C. Roads in Berland time limit per test 2 seconds memory limit per test 256 megabytes input standard ...

  3. ElementUI的Upload上传,配合七牛云储存图片

    七牛云服务器的储存区域 存储区域 地域简称 上传域名 华东 z0 服务器端上传:http(s)://up.qiniup.com 华东 z0 客户端上传: http(s)://upload.qiniup ...

  4. leelazero and google colab

    https://github.com/gcp/leela-zero/blob/master/COLAB.md 左侧菜单展开,可以查看细节

  5. YTU 2866: 结构体---点坐标结构体

    2866: 结构体---点坐标结构体 时间限制: 1 Sec  内存限制: 128 MB 提交: 499  解决: 344 题目描述 定义一个表示点坐标的结构体,输入两个点的坐标,输出这两个点中点的坐 ...

  6. vue2实现自定义样式radio单选框

    先上效果 <div class="reply"> 主编已回复: <div class="radio-box" v-for="(ite ...

  7. jQuery制作信息提示弹出层插件【推荐】

    给大家分享一款非常实用的弹窗提示窗口插件,包含多种模式.带有回执函数值的功能.​1. [代码][JavaScript]代码 <script type="text/javascript& ...

  8. 根据用户时区显示当地时间 javascript+php

    在跨时区应用中会用到下面代码,这是以前写的一段代码. 服务器保存相关时间配置,保存形式为GMT时间,客户端需要根据客户所在时区做相应显示,以符合客户习惯. ​1. [代码][JavaScript]代码 ...

  9. poj 1195 Mobile phones 解题报告

    题目链接:http://poj.org/problem?id=1195 题目意思:有一部 mobie phone 基站,它的面积被分成一个个小正方形(1 * 1 的大小),所有的小正方形的面积构成了一 ...

  10. windows server2012之部署HTTPS安全站点

    现在的互联网越来越重视网络安全方面的内容,像我们日常生活中浏览的网上银行网站等涉及安全的你都会发现有https 的标志出现,在URL前加https://前缀表明是用SSL加密的. 你的电脑与服务器之间 ...