如何使用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行小编我习惯先通篇来看看 代 ...
随机推荐
- .Net TCP探索(一)——TCP服务端开发(同时监听多个客户端请求)
最近在园子里看了大神写的(面试官,不要再问我三次握手和四次挥手),忍不住写段程序来测试一番. 在网上找了很多例子,大多只实现了TCP点对点通讯,但实际应用中,一个服务器端口往往要监听多 ...
- JNI静态注册与动态注册详解
JNI注册,是指将java层方法(native关键字修饰的)和C层方法对应起来,以实现java层代码调用c层代码的目的.JNI注册分为静态注册和动态注册两种,静态注册是通过固定格式方法名进行关联,动态 ...
- python程序编译成exe文件
最近越来越喜欢使用python写工具.使用的时候,发现程序内部成员python安装目录常常不同,如果用bat双击执行,常常需要修改从svn上down下来的bat文件中python.exe的路径.而给策 ...
- Python3实战spark大数据分析及调度 ☝☝☝
Python3实战spark大数据分析及调度 ☝☝☝ 一.实例分析 1.1 数据 student.txt 1.2 代码 二.代码解析 2.1函数解析 2.1.1 collect() RDD的特性 在 ...
- 那些你不知道的HTML知识,快来学习一下吧
前言 HTML作为前端三大基础知识点之一,是每一个前端开发人员都要掌握的部分.今天这篇文章我们来看看一些平时不太会注意,却在面试时可能会问到的题目,来看看你都会吗? 如何使用div模拟实现textar ...
- 【Labview入门】子VI的调用
labview版本:Labview2015 Labview里面也可以向C语言等变成语言一样调用子程序,本文带你一步一步实现子VI的创建与调用. 第一步 写好程序 首先我们写个简单的加法程序: 我们的目 ...
- 基于canvas实现钟表
原理说明 1.通过arc方法实现钟表外环: 2.通过line实现钟表时针,分针,秒针和刻度标志的绘制,基于save和restore方法旋转画布绘制不同角度的指针: 3.通过font方法实现在画布上绘制 ...
- shell基本运算符(五)
shell支持多种运算符,包括:算术运算符.关系运算符.布尔运算符.字符串运算符.文件测试运算符. 注意:条件表达式要放在方括号之间,并且要有空格,eg:[$a==$b] 是错误的,必须写成 [ $a ...
- RIDE-工程、测试套件、测试用例三者关系
理论 type的选择: 一般来说:测试项目(directory)-测试套件(file)-测试用例 本质上,“测试项目”和“测试套件”并没有什么区别,但是testcase只能放在file类型的test ...
- MFC中如何分割CString类型的数据
[才疏学浅,难免有纰漏,若有不正确的地方,欢迎指教] MFC中有一个库函数 Tokenize(); 函数原型:CStringT Tokenize( PCXSTR pszTokens , int& ...