redux原来如此简单

Redux 是 JavaScript 状态容器, 提供可预测化的状态管理。

那什么是可以预测化,我的理解就是根据一个固定的输入,必然会得到一个固定的结果。

redux是专门为react开发的,但并不是只能用于react,可以用于任何界面库。

动机

随着单页面应用的普及,web app内部需要管理的状态越来越多,这些状态可能来自服务器端,用户输入的数据,用户交互数据,当前UI状态,本地的缓存数据等等。如何能够有条理的管理这些数据,成为前端开发中一个难题。

核心概念

三大原则

单一数据源

使用redux的程序,所有的state都存储在一个单一的数据源store内部,类似一个巨大的对象树。

state是只读的

state是只读的,能改变state的唯一方式是通过触发action来修改

使用纯函数执行修改

为了描述 action 如何改变 state tree , 你需要编写 reducers。

reducers是一些纯函数,接口当前state和action。只需要根据action,返回对应的state。而且必须要有返回。

一个函数的返回结果只依赖于它的参数,并且在执行过程里面没有副作用,我们就把这个函数叫做纯函数

基础

action

顾名思义,action就是动作,也就是通过动作来修改state的值。也是修改store的唯一途径。

action本质上就是一个普通js对象,我们约定这个对象必须有一个字段type,来表示我们的动作名称。一般我们会使用一个常量来表示type对应的值。

此外,我们还会把希望state变成什么样子的对应的值通过action传进来,那么这里action可能会类似这样子的

{type:'TOGGLE_TODO',    index:5}

Reducer

Action 只是描述了有事情发生了这件事实,但并没有说明要做哪些改变,这正是reducer需要做的事情。

Reducer作为纯函数,内部不建议使用任何有副作用的操作,比如操作外部的变量,任何导致相同输入但输出却不一致的操作。

如果我们的reducer比较多,比较复杂,我们不能把所有的逻辑都放到一个reducer里面去处理,这个时候我们就需要拆分reducer。

幸好,redux提供了一个api就是combineReducers Api。

store

store是redux应用的唯一数据源,我们调用createStore Api创建store。

脱离react的redux案例

store,reducer基础使用

第一步搭建开发环境,这里不介绍了,参考上一篇文章手把手教会使用react开发日历组件,搭建环境部分

搭建好环境切换到目录下面

npminstallredux--save

把index.tsx修改为之下代码。

import{ createStore, combineReducers, applyMiddleware }from'redux'varsimpleReducer =function(state = {}, action){return{user: {name:'redux'}  }}varstore = createStore(simpleReducer)console.log(store.getState())

我们看到控制台打印出来的一个包含user信息的这么一个对象。

我们使用到了几个api?createStore创建store,store.getState()获取store,也就是唯一数据源的根节点。

上文我们也讲过,action的情况可能会比较多,redux也提供了combineReducersApi。如果我们有多个reducer,我们就可以使用起来了。

那我们创建多个reducer测试一下,代码如下:

import{ createStore, combineReducers, applyMiddleware } from'redux'functionuser(state = {name:'redux'},action) {  switch (action.type) {case'CHANGE_NAME':return{        ...state,name:action.name}  }returnstate}functionproject(state = {name:'min-react'},action) {  switch (action.type) {case'CHANGE_NAME':return{        ...state,name:action.name}  }returnstate}var rootReducer = combineReducers({  user,  project})var store = createStore(rootReducer)console.log(store.getState())

如我们所预料一样,我们得到拥有两个字段的根store。

结合view使用

第一步我们把html改造成这个样子,新增了一点标签

<!DOCTYPE html>Document* {margin:0;padding:0;        }更改userName

第二步,修改index.tsx,如下

import{ createStore, combineReducers, applyMiddleware }from'redux'import{ func }from'prop-types'functionuser(state = {name:'redux'}, action){switch(action.type) {case'CHANGE_USER_NAME':return{        ...state,name: action.name      }  }returnstate}functionproject(state = {name:'min-react'}, action){switch(action.type) {case'CHANGE_PROJECT_NAME':return{        ...state,name: action.name      }  }returnstate}varrootReducer = combineReducers({  user,  project})varstore = createStore(rootReducer)functionrender(state = store.getState()){var$userName =document.getElementById('userName')  $userName.innerHTML = state.user.name}render()console.log(store.getState())

我们看到页面正确的显示了我们user的名称。下一步我们需要做的就是通过用户的操作,改变store的值,进而触发view的更新。

于是我们新增了这块代码:

store.subscribe(function(){  render()})// 绑定用户事件var$userNameInput =document.getElementById('userNameInput')varuserNameButton =document.getElementById('userNameButton')userNameButton.onclick =function(){varvalue = $userNameInput.value  store.dispatch({type:'CHANGE_USER_NAME',name: value  })}

我们看到保存之后,当我们输入值之后,点击更改,页面的值随着改变。

但是控制台报了一个错误,TS2339: Property 'value' does not exist on type 'HTMLElement'.,这是由于typescript强类型校验没通过导致的。只要加这段代码就好了

var$userNameInput =document.getElementById('userNameInput')asHTMLInputElement

看到了吧,redux就是这么简单。

其他所有上层应用,都是在此基础上开发的,所以开发一个redux应用的步骤就是

定义action和与之对应的reducer

监听store的变化,提供回调函数

dispatch一个action,等待好运发生。

结合react,其他view类库,开发步骤莫不如此。

高级应用

异步action

我们也看到了,我们的reducer只能做同步应用,如果我们需要在reducer,做一些延迟操作,可怎么办

社区已经有成熟的类库做这件事件

npminstallredux-thunk--save

redux本身已经提高了很好的扩展机制,就是中间件。这点很类似express的中间件。

//引入新的类库import{ createStore, combineReducers, applyMiddleware, compose }from'redux'importthunkfrom'redux-thunk'...//store部分做如下修改constfinalCreateStore = compose(applyMiddleware(thunk))(createStore)conststore = finalCreateStore(rootReducer, {})

redux-thunk的作用就是让dispatch方法不仅仅只接收action对象,还可以包含一个方法。我们可以在这个方法内部去调用异步代码

我们把dom事件部分做了如下改造

userNameButton.onclick =function(){  varvalue= $userNameInput.valuestore.dispatch(function(dispatch, getState){    setTimeout(() => {      dispatch({type:'CHANGE_USER_NAME',name:value})    },2000)  })}

可以看到页面元素确实在2s之后发生了变化,实际业务中啊,我们这里可以做一些异步操作。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,332评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,508评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,812评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,607评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,728评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,919评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,071评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,802评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,256评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,576评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,712评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,389评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,032评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,026评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,473评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,606评论 2 350

推荐阅读更多精彩内容