如何使用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行小编我习惯先通篇来看看 代 ...
随机推荐
- Mac安装Command Line Tools
从App Store上下载的Xcode,默认是不会安装Command Line Tools的,Command Line Tools是在Xcode中的一款工具,可以在命令行中运行C程序. 在终端中输入命 ...
- Three Key Points of Success 成功三要素
Everyone wants to be successful. Today I would like to share three simple key points of success. Num ...
- ToShowDoc拯救不想写文档的你
ToShowDoc拯救不想写文档的你 写注释已经够折磨开发者了,显然天天curd的我们再去写文档岂不是分分种要被逼疯. 我想每个人都有这种经历 加了一个参数文档忘了更新 参数名更改文档忘了更新 删掉一 ...
- Mybatis入门简版(补充)
一.Mybatis 中$与#的区别 #相当于对数据 加上 双引号,$相当于直接显示数据 1. #将传入的数据都当成一个字符串,会对自动传入的数据加一个双引号.如:order by #user_id#, ...
- 蓝牙TWS耳机IBRT的原理初分析
最近在倒腾TWS对耳的一些东西,看到一些源码,发现一个新概念,IBRT没有搞清楚,抱着吾将上下而求索的态度,详细看了一些代码,查了一些资料,还是发现了不少有价值的信息的.至少,我突然感觉自己懂了一些什 ...
- cobalt strike和metasploit结合使用(互相传递shell会话
攻击机 192.168.5.173 装有msf和cs 受害机 192.168.5.179 win7 0x01 msf 派生 shell 给 Cobalt strike Msfvenom生成木马上线: ...
- 浑身尖刺的服务可用性守护者——hystrix熔断器实践记录
netflix公司的产品hystrix(长满刺的豪猪),在高可用目标下具有一定熔断.限流.降级的作用.这里主要写一些自己在使用时的问题解决思路,原理请自行理解,包括线程池与信号量模式等. 注意三个参数 ...
- javascript学习总结之Object.assign()方法详解
最近再写ES6的文章时候发现自己对Object.assign()方法不太了解,之前也没有接触过所以就就查阅了相关的资料,为了自己以后肯能会用到以及对知识进行巩固,所以在这里记录下自己学习的点点滴滴,毕 ...
- Feign服务调用请求方式及参数总结
前言 最近做微服务架构的项目,在用feign来进行服务间的调用.在互调的过程中,难免出现问题,根据错误总结了一下,主要是请求方式的错误和接参数的错误造成的.在此进行一下总结记录.以下通过分为三种情况说 ...
- Java学习笔记十二--集合(三)
第一节课 返回值 方法名 作用 void add(index,elemnet) 在指定的索引处添加元素 object get(index) 返回指定索引处的元素 int indexOf(object) ...