React初识整理(五)--Redux和Flux(解决状态传递问题)

Flux

一、引入:在React的应⽤中,状态管理是⼀个⾮常重要的⼯做。咱们不会直接对DOM节点进⾏操做,⽽是经过将数据设置给state,由state来同步UI,这种⽅式有个潜在的问题,每一个组件都有独⽴的state,而且不能相互传递。若是从⼀个组件将数据传递给另⼀个组件,须要经过props。⽽props的特色是⾃顶⽽下的传递,那么⼦组件要传递给⽗组件就会⽐较麻烦。当这种需求愈来愈多后,状态管理就会变得更加困难。react

 

二、定义:它不是⼀种⼯具或框架,⽽是⼀种架构模式。它把全部的数据都集中放在了⼀个叫store的对象中。之后每⼀个组件都不在直接操做state,⽽是把更改数据的命令封装成action,而后dispatch给store。 store再完成state的更改,对应的组件 完成UI更新。ios

三、核心概念:ajax

   - store:至关于数据中⼼,能够有多个npm

   - action:操做命令redux

   - dispatch:分发action的对象axios

   - view:视图组件服务器

  - 过程:view -> action -> dispatch -> store -> view antd

Redux

一、Flux的实现,也扩展了自身。它能够用于任何组件化开发中,比较常见用于React中。架构

  ①核心概念:app

     - store:数据中⼼,只有⼀个

     - action:操做命令 - action creator:建立命令的⽅法

     - dispatch:分发action的对象 - middleware:中间件

     - reducer:更新state数据的⽅法

     - view:视图

   ②流程:view -> action creator -> action -> dispatch -> middleware -> reducer -> -> state -> view

   ③reducer必须是1个纯函数,就是函数里的数据没有反作用的,每调用1次函数的结果必须是一致的,且只能操做state,好比函数里作加法,那么就只能作加法,而不能一下子加法一下子减法。若是函数里有异步操做,就会改变函数的操做方法,这样就不会纯函数。

二、具体用法: 安装: npm i redux npm i react-redux

  ①编写action creator。 在src下建立actions文件夹,actions下有index.js,用于储存全部的action操做命令。

/src/actions/index.js
 export const setVisible = (visible) => {
    return {
        // type必须写,是命令的名称,必须全大写,中间用_链接
        type: 'ADD_TODO',  
        visible
    }
}

  ②编写reducer:在src文件夹下建立reducers文件夹,而后在此文件下创建index.js,用于将全部的更新state数据的方法reducer都合并到一块儿,而后建立1个componentReducer.js文件存放单一的1个reducer。 将传送过来的值设置到store的state里:

// /src/reducers/componentReducer.js
// 传入第一个参数state,刚开始可能什么都没有,因此给个默认初始值
// 传入第二个参数action,即命令,它包含了传过来的type和state的值。1个命令只改1个属性

export default (state = { visible: false }, action) => {
    switch (action.type) {
            
        // 咱们不直接修改state的值,而是返回1个新的对象,这样在作优化时即使浅比较也会更新。
        // 浅比较:赋对象的变量储存的是地址,当咱们改属性的值时,变量储存的地址没变,这样浅比较时就认为它是没变化的,从而在优化时不会更新后续组件
        // 返回用ES6的扩展运算符,对象里只能有惟一键,相同的会替换。
        
        case "SET_VISIBLE":
            return {...state,visible:action.visible}
        default:
            return state;
    }
}

  将多个reducer合并为一个:

// /src/reducers/index.js
// 将全部的更新state数据的方法reducer都合并到一块儿
import { combineReducers } from 'redux';
import componentReducer from './componentReducer'
import updateStudentReducer from './updateStudentReducer'
export default combineReducers({
    componentReducer,
    updateStudentReducer
})

  ③将reducer组合到store中:用到react-redux的Provider组件,将其设置成根组件,而后将store设置给它。

// /src/store/index.js
// store主要把reducer结合进来

import React from 'react';
import reducers from '../reducers';
import { Provider } from 'react-redux'
// 建立store
import { createStore} from 'redux'
let store = createStore(reducers)

// ⽤到react-redux的Provider组件,将其设置成根组件,而后将store设置给它
export default (props) => {
    return <Provider store={store}>
        {/* 将入口的组件传进来*/}
        {props.children}
    </Provider>
}

咱们在src下创建了index.js,做为项目的主入口,而后用< Store>将入口装起来,做为全部子组件的根级元素。

// /src/index.js
import React from "react"
import ReactDOM from "react-dom"
import Store from "./antd/store"
import Router from "./router"
// 把store做为全部组件的根组件
ReactDOM.render(<Store><Router /></Store>, document.getElementById("root"));

到这里,咱们就把基础配置完成,下面就是如何使用它了。

 

  ④将组件和redux关联起来:咱们先把子组件和redux关联起来,也就是将子组件和store创建联系。在这里咱们使用react-redux下的connect方法

import { connect } from "react-redux"
class StudentsList extends Component {
    //省略
}
// 关联当前组件StudentsList到store后默认导出。关联以后props里才有dispatch方法
export default connect()(StudentsList)

connect()()方法后置两个圆括号, - 第一个圆括号用于映射,圆括号里能够是一个回调函数,自带1个参数store,它存储着全部的state状态,咱们能够直接调用。映射成功了的子组件中,就能够直接经过 props来获取state里的值了。如:this.props.visible就能够得到我存储在store里的visible的值 - 第二个圆括号用于关联,圆括号里写1个要关联的组件名。即将子组件关联到store上。关联以后子组件中的props才能dispatch方法,用于操做命令的分发。如:

import { setVisible } from "../actions"
  // 使用dispatch分发action对象到setVisible,并设置值为true,这样就调用了store里面的actions操做命令里的setVisible命令
this.props.dispatch(setVisible(true))

这样咱们就能够经过dispatch来分发action命令,从而调用以前咱们写的命令setVisible来改变对应state的状态了。

  ⑤异步操做存在问题及解决

  在⼀般状况下,都是发出action后,由reducer完成state的计算,而后更新组件。可是若是遇到 有异步操做怎么办呢?reducer是纯函数,不适合作除设置state之外的其余操做。 action creator的⽅法要求返回的是⼀个命令对象,异步操做在这⾥也有问题。 咱们来看1个例子:

// /src/actions/index.js
// 当要发送请求时,由于axios是个异步操做,它会先return,那么返回的student里就没有值,全部在这里要引入中间件thunk
import axios from "axios";
export const setStudent = (id) => {
    let student;
    axios({
        method:"get",
        url:"/students/"+id
    }).then(({data})=>{
        student = data
    })
    return {
        type: 'SET_VISIBLE',  
        student
    }
}

  我用axios发送ajax向服务器请求数据,因为ajax是异步操做,那么return会比student = data(将取回的数据赋值给student)先执行,结果咱们就return的student结果只能为undefined。return放到then方法里面也不行,若是放里面就没法说清return是针对哪一个地方的了。那么怎么解决呢?

   redux提供了中间件来解决这个问题。咱们引入中间件:redux-thunk, 首先咱们将前面的store下的index.js进行修改:

// store主要把reducer结合进来

import React from 'react';
import reducers from '../reducers';
import { Provider } from 'react-redux'

// 引入异步操做的中间插件redux-thunk。安装:npm i redux-thunk
import thunk from 'redux-thunk'
// 建立store,而后用 applyMiddleware来应用中间插件
import { createStore, applyMiddleware } from 'redux'
let store = createStore(reducers, applyMiddleware(logger,thunk))

// ⽤到react-redux的Provider组件,将其设置成根组件,而后将store设置给它
export default (props) => {
    return <Provider store={store}>
        {/* 将入口的传进来 */}
        {props.children}
    </Provider>
}

在这里,引入了中间件thunk,并将这个中间件应用到了reducers里面。 而后咱们调用axios来发送异步请求:

// /src/actions/index.js
import axios from "axios";
export const setStudent = (id) => {
    return (dispatch, getState) => {
        axios({
            method: "get",
            url:"/students/"+id
        }).then(({ data }) => {
        //应用中间件后,这里可使用dispatch来发送action命令更新数据
            dispatch({
                type: 'SET_STUDENT',
                updateStudent: data
            });
        })
    }
}

获得数据后,咱们在须要用到的组件里进行映射:

// /src/students/updateStudents.js
export default connect(
    // 简化代码 ,箭头函数中的圆括号()就是return的意思
    // ({ componentReducer: { visible } }) => ( visible )
    // 等同于:
    (store) => {
        return {
            visible: store.componentReducer.visible,
            updateStudent:store.updateStudentReducer.updateStudent
        }}
)(
    Form.create({
        // antd里的解决受控组件的方法
        //在这里咱们就用上面映射回来的值设置到对应的输入框了。
        mapPropsToFields(props) {
            return {
                name: Form.createFormField({
                    value: props.updateStudent.name
                }),
                age: Form.createFormField({
                    value: props.updateStudent.age
                }),
                gender: Form.createFormField({
                    value: props.updateStudent.gender
                }),
            };
        }
    })(UpdateStudents)
)

  ⑥在这里补充1个使用的中间件logger

logger是一个日志处理的中间件,它会自动打印操做时间的先后值的变化。

其中 prev state的内容为操做前的内容, next state为操做后的内容,这样方便对比。

下载: npm i redux-logger.

使用:

import    {createLogger}    from 'redux-logger';
const    logger    =    createLogger();
let    store    =    createStore(reducers,applyMiddleware(logger))
相关文章
相关标签/搜索