初识Redux之旅(一)

181 阅读5分钟

Redux概述

Redux是一种用来对数据状态进行管理的JavaScript应用工具,随着SPA单页面应用的复杂化,JavaScript需要管理的状态愈来愈多。这时,Redux应运而生,用来降低状态管理的复杂程度。接下来通过一张图来简单介绍redux是如何减低状态管理的复杂程度。(该图片来源于网上,如有侵权,请联系删除)

先看左边,左边是没有使用redux进行组件传值,要想深蓝色组件与最顶层组件通信,深蓝色组件必须要先跟它的父组件通信,然后它的父组件再与上一级组件通信直到最顶层。如果通过这种方式进行数据传递,那么代码将变得不可维护。

右边是使用了redux的组件传值。在redux中所有组件的数据都放在store中,在组件中少放或者不放数据。这个时候深蓝色组件与其他通信,只需改变store里面的数据,其他组件会自己感知store发生变化,自动去取store里面的数据,这样蓝色组件就非常轻松的将数据传递到其他组件。

Redux工作流程

1.Redux工作流程简述

接下来通过官方提供的一张图,我尝试解释一下redux的工作流程。

所有的数据都存放在store中,而所有的组件都会从store中拿取数据。以图书馆借书为例,来理解下这张图。首先React Components说我想借书,这时它就将借书这个事情告诉了Action Creators。Action Creators把这句话告诉store,然后图书馆管理员(store)听到了这句话,他就会去翻取他的记录本(reducers),找到之后管理员再把这本书交给React Components。

2.安装Redux

    npm install redux --save

3.创建store

在store文件夹下创建一个index.js文件 store仅仅是一个仓库,它不具备管理能力,将接收到的action自动转发给reducer。

import { createStore } from 'redux'  // 引入Redux
import reducer form 'reducer'
const store = createStore(reducer) // 创建一个数据仓库
export default store  // 将store暴露出去

4.创建action

要想改变Redux里面state数据的值,就需要创建一个action,action是一个对象,里面一般有两个属性,第一个是action的描述,第二个是想要改变的值,然后再通过dispatch()方法传递给store。

import React from 'react'
import store from "./store"
import { Input, Button } from 'antd'
import 'antd/dist/antd.css'

class Test extends React.Component{
  constructor(props) {
    super(props)
    // 通过store.getState()获取store里面的数据
    this.state = store.getState()
    // 改变this的指向
    this.handlerInput = this.handlerInput.bind(this)
  }
  render() {
    return (
      <div>
        <Input
          value={this.state.inputValue}
          onChange={this.handlerInput}
        />
      </div>
    )
  }
  handlerInput(e){
    const action = {
        type: 'change_value',
        value: e.target.value
    }
    store.dispatch(action)
  }
}

5.创建reducer

// 这个文件就相当于是上图中的reducers部分
// 定义一个默认值
const defaultState = {
  inputValue: ''
}
// 这个方法接收两个参数,一个是state所有需要管理的原始数据信息
// 一个是action,指的是action传递的新状态
// 注意:reducer可以接收state,但是绝不能直接改变state,改变state还是store中
export default (state = defaultState, action) => {
  if (action.type === 'change_value') {
     const newState = JSON.parse(JSON.stringify(state)) //深度拷贝state
     newState.inputValue = action.value
     return newState
  }
  return state
}

通过以上几个步骤,便可实现一个简单的Redux流程。通过上面的方式会存在一些问题:

  1. 在开发过程中,肯定会派发多个action,这样就会造成一系列问题。如果不统一管理,可能会造成代码难以维护。action中的type必须要跟reducer里面action的type保持一致,如果名称不一致,浏览器不会报错,这样很难定位bug的位置。

  2. 对于逻辑复杂的组件,可能会存在很多action,将这些action都放在一个组件里面,很非常冗余,导致代码可读性差。

代码优化

针对以上的问题,我们应当采取统一的管理方式,这样可以提高代码的可读性、可维护性也会非常高。

1.创建一个store文件夹,在里面创建一个actionTypes.js文件

export const CHANGE_INPUT = 'CHANGE_INPUT'

2.在store文件夹下创建一个actionCreators.js文件

import {CHANGE_INPUT} form './actionTypes'
export const changeInput = (value) => ({
  type: CHANGE_PAGE,
  value
})

3.对action部分代码稍作改动

import React from 'react'
import store from "./store"
import { Input, Button } from 'antd'
import 'antd/dist/antd.css'
import {CHANGE_INPUT} form './actionTypes'
import {CHANGE_INPUT} form './actionCreators'

class Test extends React.Component{
  constructor(props) {
    super(props)
    // 通过store.getState()获取store里面的数据
    this.state = store.getState()
    // 改变this的指向
    this.handlerInput = this.handlerInput.bind(this)
  }
  render() {
    return (
      <div>
        <Input
          value={this.state.inputValue}
          onChange={this.handlerInput}
        />
      </div>
    )
  }
  handlerInput(e){
    const action = changeInput(e.target.value)
    store.dispatch(action)
  }
}

4.reducer部分

import {CHANGE_INPUT} form './actionTypes'
// 这个文件就相当于是上图中的reducers部分
// 定义一个默认值
const defaultState = {
  inputValue: ''
}
// 这个方法接收两个参数,一个是state所有需要管理的原始数据信息
// 一个是action,指的是action传递的新状态
// 注意:reducer可以接收state,但是绝不能直接改变state,改变state还是store中
export default (state = defaultState, action) => {
  if (action.type === 'CHANGE_INPUT') {
     const newState = JSON.parse(JSON.stringify(state)) //深度拷贝state
     newState.inputValue = action.value
     return newState
  }
  return state
}

Redux注意事项

  • store是必须唯一的,只能有一个store仓库
  • 只有store能改变数据,reducer不能直接改变数据。reducer只是做了一个返回,并未真正改变数据,在store拿到reducer返回的数据之后,自己对自己做了改变,然后再更新到页面上。
  • reducer里面必须是一个纯函数,所谓的纯函数就是给定固定的输入,就一定会有固定的输出,且不会有任何的副作用。