如何使用24行JavaScript代码实现Redux
作者:Yazeed Bzadough
译者:小维FE
原文:freecodecamp
为了保证文章的可读性,本文采用意译而非直译。
90%的规约,10%的库。
Redux是迄今为止创建的最重要的JavaScript库之一,灵感来源于以前的艺术比如Flux和Elm,Redux通过引入一个包含三个简单要点的可伸缩体系结构,使得JavaScript函数式编程成为可能。如果你是初次接触Redux,可以考虑先阅读官方文档。
1. Redux大多是规约
考虑如下这个使用了Redux架构的简单的计数器应用。如果你想跳过的话可以直接查看Github Repo。
1.1 State存储在一棵树中
该应用程序的状态看起来如下:
const initialState = { count: 0 };
1.2 Action声明状态更改
根据Redux规约,我们不直接修改(突变)状态。
// 在Redux应用中不要做如下操作
state.count = 1;
相反,我们创建在应用中用户可能用到的所有行为。
const actions = {
increment: { type: 'INCREMENT' },
decrement: { type: 'DECREMENT' }
};
1.3 Reducer解释行为并更新状态
在最后一个架构部分我们叫做Reduer,其作为一个纯函数,它基于以前的状态和行为返回状态的新副本。
- 如果
increment
被触发,则增加state.count
- 如果
decrement
被触发,则减少state.count
const countReducer = (state = initialState, action) => {
switch (action.type) {
case actions.increment.type:
return {
count: state.count + 1
};
case actions.decrement.type:
return {
count: state.count - 1
};
default:
return state;
}
};
1.4 目前为止还没有Redux
你注意到了吗?到目前为止我们甚至还没有接触到Redux库,我们仅仅只是创建了一些对象和函数,这就是为什么我称其为"大多是规约",90%的Redux应用其实并不需要Redux。
2. 开始实现Redux
要使用这种架构,我们必须要将它放入到一个store当中,我们将仅仅实现一个函数:createStore
。使用方式如下:
import { createStore } from 'redux'
const store = createStore(countReducer);
store.subscribe(() => {
console.log(store.getState());
});
store.dispatch(actions.increment);
// logs { count: 1 }
store.dispatch(actions.increment);
// logs { count: 2 }
store.dispatch(actions.decrement);
// logs { count: 1 }
下面这是我们的初始化样板代码,我们需要一个监听器列表listeners和reducer提供的初始化状态。
const createStore = (yourReducer) => {
let listeners = [];
let currentState = yourReducer(undefined, {});
}
无论何时某人订阅了我们的store,那么他将会被添加到listeners
数组中。这是非常重要的,因为每次当某人在派发(dispatch)一个动作(action)的时候,所有的listeners
都需要在此次事件循环中被通知到。调用yourReducer
函数并传入一个undefined
和一个空对象将会返回一个initialState
,这个值也就是我们在调用store.getState()
时的返回值。既然说到这里了,我们就来创建这个方法。
2.1 store.getState()
这个函数用于从store中返回最新的状态,当用户每次点击一个按钮的时候我们都需要最新的状态来更新我们的视图。
const createStore = (yourReducer) => {
let listeners = [];
let currentState = yourReducer(undefined, {});
return {
getState: () => currentState
};
}
2.2 store.dispatch()
这个函数使用一个action
作为其入参,并且将这个action
和currentState
反馈给yourReducer
来获取一个新的状态,并且dispatch
方法还会通知到每一个订阅了当前store的监听者。
const createStore = (yourReducer) => {
let listeners = [];
let currentState = yourReducer(undefined, {});
return {
getState: () => currentState,
dispatch: (action) => {
currentState = yourReducer(currentState, action);
listeners.forEach((listener) => {
listener();
});
}
};
};
2.3 store.subscribe(listener)
这个方法使得你在当store接收到一个action
的时候能够被通知到,可以在这里调用store.getState()
来获取最新的状态并更新UI。
const createStore = (yourReducer) => {
let listeners = [];
let currentState = yourReducer(undefined, {});
return {
getState: () => currentState,
dispatch: (action) => {
currentState = yourReducer(currentState, action);
listeners.forEach((listener) => {
listener();
});
},
subscribe: (newListener) => {
listeners.push(newListener);
const unsubscribe = () => {
listeners = listeners.filter((l) => l !== newListener);
};
return unsubscribe;
}
};
};
同时subscribe
函数返回了另一个函数unsubscribe
,这个函数允许你当不再对store的更新感兴趣的时候能够取消订阅。
3. 整理代码
现在我们添加按钮的逻辑,来看看最后的源代码:
// 简化版createStore函数
const createStore = (yourReducer) => {
let listeners = [];
let currentState = yourReducer(undefined, {});
return {
getState: () => currentState,
dispatch: (action) => {
currentState = yourReducer(currentState, action);
listeners.forEach((listener) => {
listener();
});
},
subscribe: (newListener) => {
listeners.push(newListener);
const unsubscribe = () => {
listeners = listeners.filter((l) => l !== newListener);
};
return unsubscribe;
}
};
};
// Redux的架构组成部分
const initialState = { count: 0 };
const actions = {
increment: { type: 'INCREMENT' },
decrement: { type: 'DECREMENT' }
};
const countReducer = (state = initialState, action) => {
switch (action.type) {
case actions.increment.type:
return {
count: state.count + 1
};
case actions.decrement.type:
return {
count: state.count - 1
};
default:
return state;
}
};
const store = createStore(countReducer);
// DOM元素
const incrementButton = document.querySelector('.increment');
const decrementButton = document.querySelector('.decrement');
// 给按钮添加点击事件
incrementButton.addEventListener('click', () => {
store.dispatch(actions.increment);
});
decrementButton.addEventListener('click', () => {
store.dispatch(actions.decrement);
});
// 初始化UI视图
const counterDisplay = document.querySelector('h1');
counterDisplay.innerHTML = parseInt(initialState.count);
// 派发动作的时候跟新UI
store.subscribe(() => {
const state = store.getState();
counterDisplay.innerHTML = parseInt(state.count);
});
我们再次看看最后的视图效果:
原文: https://www.freecodecamp.org/news/redux-in-24-lines-of-code/
4. 交流
本篇主要简单了解下Redux的三个架构组成部分以及如何实现一个简化版的Redux,对Redux能有进一步的了解,希望能和大家相互讨论技术,一起交流学习。
文章已同步更新至Github博客,若觉文章尚可,欢迎前往star!
你的一个点赞,值得让我付出更多的努力!
逆境中成长,只有不断地学习,才能成为更好的自己,与君共勉!
如何使用24行JavaScript代码实现Redux的更多相关文章
- 60行JavaScript代码俄罗斯方块
教你看懂网上流传的60行JavaScript代码俄罗斯方块游戏 早就听说网上有人仅仅用60行JavaScript代码写出了一个俄罗斯方块游戏,最近看了看,今天在这篇文章里面我把我做的分析整理一下( ...
- 65行 JavaScript 代码实现 Flappy Bird 游戏
飞扬的小鸟(Flappy Bird)无疑是2014年全世界最受关注的一款游戏.这款游戏是一位来自越南河内的独立游戏开发者阮哈东开发,形式简易但难度极高的休闲游戏,很容易让人上瘾. 这里给大家分享一篇这 ...
- 教你看懂网上流传的60行JavaScript代码俄罗斯方块游戏
早就听说网上有人仅仅用60行JavaScript代码写出了一个俄罗斯方块游戏,最近看了看,今天在这篇文章里面我把我做的分析整理一下(主要是以注释的形式). 我用C写一个功能基本齐全的俄罗斯方块的话,大 ...
- 只有20行Javascript代码!手把手教你写一个页面模板引擎
http://www.toobug.net/article/how_to_design_front_end_template_engine.html http://barretlee.com/webs ...
- 9 行 javascript 代码获取 QQ 群成员
昨天看到一条微博:「22 行 JavaScript 代码实现 QQ 群成员提取器」. 本着好奇心点击进去,发现没有达到效果,一是 QQ 版本升级了,二是博客里面的代码也有些繁琐. 于是自己试着写了一个 ...
- 只要200行JavaScript代码,就能把特斯拉汽车带到您身边
Jerry的前一篇文章 如何使用JavaScript开发AR(增强现实)移动应用 (一) 介绍了用React-Native + ViroReact开发增强现实应用的一些预备知识. 本文咱们开始进入增强 ...
- 【转】265行JavaScript代码的第一人称3D H5游戏Demo
译文:http://blog.jobbole.com/70956/ 原文:http://www.playfuljs.com/a-first-person-engine-in-265-lines/ 这是 ...
- 150行JavaScript代码实现增强现实
增强现实技术(Augmented Reality,简称 AR),是一种实时地计算摄影机影像的位置及角度并加上相应图像.视频.3D模型的技术,这种技术的目标是在屏幕上把虚拟世界套在现实世界并进行互动.这 ...
- 30行JavaScript代码实现一个比特币量化策略
精简极致的均线策略 30行打造一个正向收益系统 原帖地址:https://www.fmz.com/bbs-topic-new/262 没错!你听的没错是30行代码!仅仅30行小编我习惯先通篇来看看 代 ...
随机推荐
- POJ - 3646 The Dragon of Loowater
Once upon a time, in the Kingdom of Loowater, a minor nuisance turned into a major problem. The shor ...
- vue中图片放大镜功能
仿淘宝详情页图片鼠标移过去可对图片放大显示在右侧 效果图如下图,此功能支持PC端与移动端 接下来进入代码实现环节: 先准备两张图片,一张小图片叫 '土味.jpg',大小160*91:一张大图片叫 ' ...
- RF页面断言
title should be(断言title与预期指定的title内容相等): should be equal (断言某个字符串与预期指定的字符串相等) should not be equal ( ...
- 项目代码管理工具Git的总结
在项目的开发中,代码的同步管理很重要,团队的几个人可以通过免费的github管理自己的开源项目代码,高效方便.下面说说,开发中经常用到的git指令操作,基于github平台. 0.配置提交者的账户和邮 ...
- 高精度运算略解 在struct中重载运算符
高精度 高精度,即高精度算法,属于处理大数字的数学计算方法.在一般的科学计算中,会经常算到小数点后几百位或者更多,当然也可能是几千亿几百亿的大数字. 重载运算符 运算符重载,就是对已有的运算符重新进行 ...
- LeetCode初级算法--树01:二叉树的最大深度
LeetCode初级算法--树01:二叉树的最大深度 搜索微信公众号:'AI-ming3526'或者'计算机视觉这件小事' 获取更多算法.机器学习干货 csdn:https://blog.csdn.n ...
- Java线程池构造参数详解
在ThreadPoolExecutor类中有4个构造函数,最终调用的是如下函数: public ThreadPoolExecutor(int corePoolSize, int maximumPool ...
- CSAPP: 位操作实现基本运算
目录 实验要求 实现代码 1.pow2plus1 2.pow2plus4 3.bitXor 4.tmin 5.isTmax 6.allOddBits 7.negate 8.isAsciiDigit 9 ...
- 源码学习系列之SpringBoot自动配置(篇一)
源码学习系列之SpringBoot自动配置源码学习(篇一) ok,本博客尝试跟一下Springboot的自动配置源码,做一下笔记记录,自动配置是Springboot的一个很关键的特性,也容易被忽略的属 ...
- CSS盒模型以及如何解决边距重叠问题
盒模型有两种,W3C 和IE 盒子模型 W3C定义的盒模型包括margin.border.padding.content,元素的宽度width=content的宽度 IE盒模型与W3C盒模型的唯一区别 ...