上节课我们用react实现了我们的求和案例,那一节没有任何可说的,甚至可以不学那节课,但是通过那节课我们也知道了我们想要的需求究竟是什么样子的了,这节课我们就来引入redux来实现一下这个demo

redux精简版

我们说了要实现刚才哪个demo,而刚才哪个demo很简单,在react里面一个组件也就完成了,但是接下来我们要来引入redux,可能很多有学过react的人说redux好难的,那么我们先从最精简的模式来实现,我们一步步来,先别给自己太大压力。

结构精简

先来分析我们的redux工作流程的结构:

组件发起--> Action Creators --> Store --> Reducers --> Store -->组件获取新state,那么我们来想一下,Action Creators是什么?是不是创建动作对象的?那么动作对象是什么?不就是一个普通的js对象嘛,那么我们自己写这个对象行不行?是不是完全没问题的?一个type属性,一个data属性。那我们是不是就完全不需要Action Creators了?

那么Store是不是必不可少的?这个是一个核心节点啊,那么Reducers是不是也得有?不然的话没人干活了啊。

分析

我们来分析一下,我们要有store,那么我们是不是要建一个store.js文件?我们要用Reducers,那么为什么要在结尾加个s?是不是复数啊?因为每个组件都要有自己的reducer,那么我们这个是Sum组件的reducer,那么是不是就应该创建sum_reducer.js?我们要能明确这个reducer是谁的,所以把组件名带上,那么好,我们来把文件创建一下。

紧接着,我们既然要用redux那么我们是不是要安装这个库啊?脚手架可不知道我们要用这个库,所以说是不会自动帮我们安装的,那么我们来安装一下,执行命令npm i redux

Store实现

那么我们安装好了redux,那么我们也该正式来实现了,其实我们在redux中实现代码的先后顺序到时没有什么要求,但是我们建议初学的时候先来实现最核心的部分,所以我们先来实现store

那么store是个什么呢?我们光说了这个是个调度器,是个存储器,但是我们没有从本质上介绍过这是个什么。既然我们不知道这个是什么那么我们这么去实现store啊?

而且store是不是在redux整个流程的C位的?那么这么核心的一个东西,那能是我们随便写几行代码就直接搞出来的一个吗?是不是不可能啊?所以说官方在这里提供了一个API用来创建一个store

import { createStore } from 'redux';
import sumReducer from './sum_reducer';

export default createStore(sumReducer);

我们来开一下这里的代码,首先从redux库中引入一个方法createStore,那么大家说这个方法是干什么的?这是不是用脚后跟都能想的出来?这个玩意儿就是专门用来创建store的啊。那么我们调用这个方法的是传了个什么进去?我们从sum_reducer中导入了一个sumReducer,这是什么啊?这是不是一个干活的reducer啊?就好比开一个饭馆,开业的时候老板肯定要在开业前就找好厨师的团队啊。所以说这里也一样,我们创建store的时候,就要传入reducer。然后我们把创建的store暴露出去,这样我们才能够使用。

Reducer实现

上面我们实现了store,知道在创建store的时候是不是要传入一个reducer?那么我们的reducer实现了吗?我们的sum_reducer.js里面是不是什么都还没写呢?那么我们来实现我们的reducer

那么我们到现在都不知道reducer到底是个啥玩意儿,我们怎么去实现呢?首先我们来想一下,我们这个reducer都能干什么?我们不要去死记一些固定的格式,这个是个函数啊,函数要传什么参数啊,这样没有什么意义,我们先来搞懂他。先来想一想,reducer能干什么?

  1. reducer可以初始化state
  2. reducer可以接收到两个参数
  3. 当前的state
  4. store传过来的动作对象
  5. reducer可以根据接收到的参数然后生成新的state然后返回给store

那么我们来想一下,js里面什么东西可以接收参数,然后还要返回值?是不是只有函数?所以说reducer是不是应该就是一个函数啊?

export default function sumReducer(preState, action) {
  if (preState === undefined) preState = 0;
  const { type, data } = action;
  switch (type) {
    case "increment":
      return preState + data;
    case "decrement":
      return preState - data;
    default:
      return preState;
  }
}

我们来看代码,我们知道了这个东西是一个函数,而且这个函数呢要接收两个参数,一个是之前的状态,一个是动作对象,那么我们是不是要从动作对象里面拿到动作类型和数据?然后根据动作类型来判断,我们这里用了switch的控制流。一个加一个减。

可能有人要问了,那么我们说的奇数时再加还要哪个延迟 1 秒之后再加为啥没有写呢?redux里面是不关心你这些的,我就给你一个加一个减,这两个操作,你要奇数再加或者延迟 1 秒再加的话你先在组件中或者其他环节判断好,当要进行加操作的时候你再通知reducer,否则就不通知他干活。

那么我们来看我们的default。这是在干什么呢?我们之前既没有加也没有减的话应该是什么?是不是初始化?但如果初始化的时候preState进来的是不是undefined?那么我们就判断一下,如果是undefined那么我们就把preState置 0,然后在把preState返回出去。这样我们就完成了初始化的操作。

至此我们这个demoreducer也实现了。

组件使用redux

那么我们现在已经实现了store,也实现了reducer,那么我们回到组件中,我们来想一下组件中的那些onClick事件的回调方法应该这么改。

 import React, { Component } from 'react'
import store from '../../redux/store';

export default class Sum extends Component {
  render() {
    return (
      <div>
        <h1>当前求和为:{store.getState()}</h1>
        <select ref={c => this.selectNum = c}>
          <option value="1">1</option>
          <option value="2">2</option>
          <option value="3">3</option>
        </select>
        <button onClick={this.add}>+</button>
        <button onClick={this.dec}>-</button>
        <button onClick={this.odd}>odd</button>
        <button onClick={this.async}>async</button>
      </div>
    )
  }
  add = () => {}
  dec = () => {}
  odd = () => {}
  async = () => {}
}

我们先别着急看我们的方法,首先我们已经把我们展示的数据都交给redux管理了,那么我们是不是就不需要在组件内部自己定义state了?那么我们把state给删了,我们初始页面展示什么?我们是不是要找redux拿?那么redux把我们的state存在哪了?是不是在store里面?那么怎么从store里面拿state?是不是用getState方法?那么我们来卡一下效果:

image-20220118164803096

大家看,初始效果已经展示出来了,但是现在这些按钮都还不能点,因为我们的回调都还没有修改。那么我们应该怎么修改这些回调呢?

 import React, { Component } from 'react'
import store from '../../redux/store';

export default class Sum extends Component {
  render() {...}
  add = () => {
    const { value } = this.selectNum;
    store.dispatch({ type: "increment", data: value });
  }
  dec = () => {}
  odd = () => {}
  async = () => {}
}

我们来看代码,我们把其他的代码都隐藏了,就留了加的代码,如果加我们明白了,其他的都是一样的。那么加里面是什么?首先我们是不是还是要根据ref来获取选择的值?然后我们要通知redux来干活。那么谁能通知啊?我们按照流程图,是不是要通知Action Creators然后由store来居中调度分发动作对象,但是我们是不是这个demo我们不走Action Creators?我们自己手写一个动作对象,然后传出去。那么我们说过从组件中发起之后,是不是要有dispatch函数来分发动作对象,而调度器是谁?是不是store?那么我们是不是通过store.dispatch方法就可以完成动作分发了啊?我们来看一下效果:

iShot2022-01-18 17.05.05

大家看,我们点击了好多次加号了,但是页面并没有变化,为什么呢?我们来想一想,在react中只要state发生变化是不是就会重新调用render?但是redux中并没有给你这个机制,redux又不是reactredux只是帮你管理一下react的状态而已,所以说我们要在组件中监测redux中的状态,只要发生变化就重新调用render。那么我们来改一下:

import React, { Component } from 'react'
import store from '../../redux/store';

export default class Sum extends Component {
  componentDidMount(){
    store.subscribe(()=>this.setState({}));
  }
  render() {...}
  add = () => {...}
  dec = () => {...}
  odd = () => {...}
  async = () => {...}
}

我们来看一下代码,我们是不是要在组件一挂载完成就开始监测reduxstate?那么就要用componentDidMount钩子,然后调用storesubscribe方法,这个方法要接收一个函数,那么我们这个箭头函数做了什么?setState但是什么都没修改。我们是不是只要setState就会自动调render,那么我们这个箭头函数会在什么时候被调用呢?只要redux中的state一发生变化,那么这个箭头函数就会被执行。那么我们这次再来看一下结果:

iShot2022-01-18 17.21.30

这次我们便可以正常展示我们的结果了

总结

  • subscribe API来监测redux中的state
  • getState API来获取redux中的state
  • createStore来创建store
  • redux中不关心其他的东西,制作基础操作
  • redux中的state改变不重新调render

Copyright statement:The articles of this site are all original if there is no special explanation, indicate the source please when you reprint.

Link of this article:https://work.lynchow.com/article/demo_react_redux_mini/