上节课我们完成了用很精简的方式来完成了redux版的求和案例,但是上节课那是精简版的,省略了创建动作对象那一步,而且整个过程也没有严格地遵守规范,那么这节课我们来完整地用redux来实现我们之前的哪个案例。

回顾

在我们开始这节课的内容之前,我们先对上节课的内容来做一个简单的回顾。

  • subscribe API来监测redux中的state
  • getState API来获取redux中的state
  • createStore来创建store
  • reducer本质上是一个函数,接收之前的状态和动作对象
  • redux中不关心其他的东西,制作基础操作
  • redux中的state改变不重新调render

以上便是上节课的主要要点,接下来我们来开始这节课的学习。

Action Creator

我们之前精简版来使用redux的时候是不是直接手写的动作对象啊?但是呢样的话不符合redux的使用规范,正常的流程是什么样子的呢?我们是需要由组件发起,然后由Action Creators来创建一个动作对象,然后再通过dispatch分发出去的。那么我们现在既然要用到Action Creators了,我们是不是要创建一个文件来专门为我们创建动作对象啊?

而且我们看见了这个Action Creators也是复数,那么是不是和Reducers一样?每个组件要有一个对应的Action Creator,所以说我们就要在redux文件夹下创建一个sum_action_creator.js文件。我们这里说一下,因为redux也不是组件,所以说要在src下创建一个文件夹专门用于存放redux相关的文件。

那么现在我们文件创建好了,依然还是之前那样,我们不知道如何来创建一个动作对象啊。而且这个creator的本质上又是什么呢?我们来想一想,我们这个creator是用来干什么的?是不是专门为Sum组件创建动作对象的?那么我们的Sum组件都有哪些动作?是不是加和减?那么我们这里的creator只要创建了动作对象之后是不是要交出去给dispatch?那么是不是就意味着要返回一个对象给dispatch做参数啊?那么直接用函数就可以完成了啊。

export const createIncrementAction = data => ({ type: 'increment', data });

export const createDecrementAction = data => ({ type: 'decrement', data });

大家来看代码,这是什么意思?我们是不是分别暴露了两个函数,这两个函数都是箭头函数,箭头函数都接收一个参数就是我们要操作的对象,然后我们返回一个对象,这个对象就是我们的动作对象,有人可能说这两个函数长得这么像,我们给写成一个函数吧,当然可以,但是我们先这么写,我们一步一步来,我们这个案例会一步一步地进行版本迭代,到了最后一个版本我们会给大家呈现出一个最规范的版本。现在就先这么写,不要给自己太大的压力,先把族简单的原理搞懂。

那么我们再来问大家,我这个箭头函数问什么要用小括号?我们是不是要返回一个对象?对象是不是以花括号来作为边界的?而函数体是不是也是以花括号作为边界的?如果我们这里不用小括号把对象括起来的话,箭头函数就默认你这不是对象,而是函数体。

创建动作对象

现在我们已经写好了我们的Action Creator了,那么我们怎么用?首先这个Action Creator是由谁触发的?是不是我们的组件触发这里的Creator来创建动作对象的?那么我们回到组件中来:

image-20220119084518936

看看我们这是在干什么啊?我们是不是自己手写了一个动作对象交给了dispatch啊?现在呢我们既然已经准备好了Creator那么我们还要这么写吗?我们是不是直接调用我们的Creator来创建动作对象然后再交给dispatch就行了?

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

export default class Sum extends Component {
  componentDidMount() {
    store.subscribe(() => this.setState({}));
  }
  render() {...}
  add = () => {
    const { value } = this.selectNum;
    store.dispatch(createIncrementAction(value * 1));
  }
  dec = () => {
    const { value } = this.selectNum;
    store.dispatch(createDecrementAction(value * 1));
  }
  odd = () => {
    const { value } = this.selectNum;
    if (store.getState() % 2 !== 0) store.dispatch(createIncrementAction(value * 1));
  }
  async = () => {
    const { value } = this.selectNum;
    setTimeout(() => {
      store.dispatch(createIncrementAction(value * 1));
    }, 1000)
  }
}

我们来看代码,首先这些函数是不是都在sum_action_creator.js里啊?那么我们就得先导入,然后再之前手写动作对象的地方直接调用函数。这样的话我们就可以通过我们的Action Creator来创建我们的动作对象了。那么我们再来看一下效果吧

iShot2022-01-19 08.54.36

我们来看一下,这次我们是不是也可以完成我们想要的效果。

常量模块

可能大家以为我们到上面那一步我们的完整版就写完了,但是我们有一个问题,当然并不是代码上的问题,我们来想一下,我们的reducer里面匹配动作的type以及我们创建动作对象里面的type是不是都是我们手写的?那么如果不小心单词写错了怎么办?那我们不就匹配不上了嘛。所以说我们要添加一个常来那个模块在redux下创建一个constant.js文件

// constant.js
export const INCREMENT = "increment";
export const DECREMENT = "decrement";
// sum_action_creator.js
import { INCREMENT, DECREMENT } from "./constant"

export const createIncrementAction = data => ({ type: INCREMENT, data });
export const createDecrementAction = data => ({ type: DECREMENT, data });
// sum_reducer.js
import { INCREMENT, DECREMENT } from './constant';

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;
  }
}

我们来看代码,我们在constant.js里面定义了两个常量,然后分别暴露出来,然后再用到的地方直接导入常量,这样我们就不会因为单词不小心写错导致功能不起作用了。

总结

  • Action Creators本质上其实也是函数
  • 我们通过常量模块来定义动作类型,防止单词写错

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_full/