Skip to content

Redux相关

Redux概念简述

  • Redux = Reducer + Flux
    Image title
    Redux就是把组件中的数据放到公用存储区域去存储,改变数据之后同步到store,其他组件会自动感知到数据的变化,并执行响应的处理。


Redux的工作流程

  • 首先,一个组件要去获取Store里的一些数据,然后要给Store说"我要获取数据了",这句话就是Action Creators,然后通过Action Creators后Store知道了这句话,但是不知道是什么意思,所以就要去Reducers查询到底应该给组件什么样的数据。
    Image title


Redux 的核心API

  • createStore: 创建store
  • store.dispatch: 派发action, 传递到store
  • store.getState: 获取store里所有的数据内容
  • store.subscribe 订阅store的改变, 只要store发生改变, 订阅这个store的函数就会被执行


Store 的创建

  • 安装 Redux:npm install redux
  • 在src下创建store,再创建创世store与reducer,示例如下
1
2
3
4
5
6
7
import {createStore} from 'redux';
import reducer from './reducer';

// 成功创建store,把reducer传给store,这样store就知道目前有多少内容可以在reduce去查看了
const store = createStore(reducer);

export default store;
// 定义在reducer只有两个字段,后续传入到store store也就知道reducer存储内容的结构了
const defaultState = {
    inputValue: '',
    list: []
}
// 默认什么都不存储
export default (state = defaultState, action) => {
    // state: 整个store存储的数据
    return state
}


从 Store 取数据显示出来

  • store.getState()
    import React, { Component } from "react";
    import store from "./store";
    
    class TodoList extends Component {
        constructor(props) {
            this.state = store.getState()
        }
        ... ...
    }
    
    export default TodoList
    


Action 和 Reducer 的编写(完成输入框状态值渲染、list新增、list删除)

import React, { Component, Fragment } from "react";
import store from "./store";
import './style.css'
import TodoItem from "./TodoItem";

class TodoList extends Component {

    // 构造方法,固定接收props
    constructor(props) {
        // 当props发生改变,render就会被重新执行
        // 当父组件的render函数被运行时,它的子组件的render都将被重新运行
        // 调用父类的构造函数
        super(props);
        this.state = store.getState()
        this.handleInputChange = this.handleInputChange.bind(this)
        this.handleStoreChange = this.handleStoreChange.bind(this)
        // 自动订阅store,监听store的内容
        store.subscribe(this.handleStoreChange);
    }

    render() {
        return (
            <Fragment>
                <div>
                    <label htmlFor="insertArea">输入内容</label>
                    <input
                        id="insertArea"
                        className="input"
                        value={this.state.inputValue}
                        // 改变handleInputChange的this指向
                        onChange={this.handleInputChange.bind(this)}
                        // 构建一个引用,这个引用叫this.input,指向input指定当前input的dom节点
                    />
                    <button onClick={this.handleBtnClick.bind(this)}>提交</button>
                </div>
                <ul>
                    {
                        // 渲染数据
                        this.state.list.map((item, index) => {
                            return (
                                <TodoItem
                                    // 父组件向子组件传值、方法,子组件可以通过props调用
                                    itemVal={item}
                                    index={index}
                                    del={this.handleItemDelete.bind(this)}
                                />
                            )
                        })
                    }
                </ul>
            </Fragment>
        )
    }

    // componentDidMount () {
        // axios.get('/api/todolist')
        //     .then(() => {alert('succ')})
        //     .catch(() => {alert('error')})
    // }

    handleInputChange(e) {
        const value = e.target.value;
        const action = {
            type: 'change_input_value',
            value: value
        }
        store.dispatch(action)
        // this.setState({
        //     inputValue: value
        // });
    }

    handleStoreChange () {
        this.setState(store.getState())
    }

    handleBtnClick() {
        const value = this.state.inputValue
        const action = {
            type: 'change_child_value',
            value: value
        }
        store.dispatch(action);
        // this.setState({
        //     // es6 追加
        //     list: [...this.state.list, this.state.inputValue],
        //     // 提交后清空当前input
        //     inputValue: ''
        // });
    }

    handleItemDelete(index) {
        // immutable
        // state 不允许做出任何改变
        // 展开运算符 把this.state.list做了一份copy
        // const list = [...this.state.list];
        // list.splice(index, 1);
        // this.setState({
        //     list: list
        // });

        // const value = this.state.list;
        const action = {
            type: 'del_child_value',
            index: index
        }
        store.dispatch(action)
    }
}

export default TodoList
// 定义在reducer只有两个字段,后续传入到store store也就知道reducer存储内容的结构了
const defaultState = {
    inputValue: '',
    list: [],
    index: '',
}
// state = defaultState 默认什么都不存储
// reducer 可以接收state,但是绝不能修改state
export default (state = defaultState, action) => {
    // console.log(state, action)
    /*
    {inputValue: '', list: Array(0)}]:
    {type: 'change_input_value', value: 'f'}
    */
    if (action.type === 'change_input_value') {
        // 执行一次深拷贝 然后修改深拷贝的值
        const newState = JSON.parse(JSON.stringify(state));
        newState.inputValue = action.value;
        return newState;  
    }
    if (action.type === 'change_child_value') {
        const newState = JSON.parse(JSON.stringify(state));
        newState.list.push(newState.inputValue);
        newState.inputValue = '';
        console.log('change_child_value:newState', newState)
        /**
        {
            inputValue: ""
            list: ['123', '321']
        }
        */
        return newState;
    }
    if (action.type === 'del_child_value') {
        const newState = JSON.parse(JSON.stringify(state))
        console.log(newState)
        // console.log(newState, state)
        newState.list.splice(action.index, 1);
        return newState;
    }
    // state: 整个store存储的数据
    return state
}


总结

react改变store里的数据,首先要开发一个Action,Action会通过dispatch方法传递给store,store再把之前的数据和action转发给Reducer,Reducer是个函数,它接收到了state和action之后做一些处理会返回一个新的state给到store,store用这个新的数据替换掉之前store里的数据,然后store数据发生改变的时候,react组件会感知(subscribe)到store发生了改变,这个时候它从store里重新取数据,更新组件的内容。页面就跟着发生变化了。


Redux 设计和使用的三项原则

store 必须是唯一的。
只有store 能改变自己的内容。
Reducer 必须是纯函数:纯函数是指,给定固定的输入,就一定会有固定的输出,而且不会有任何副作用。


番外

ActionTypes的拆分

提示

将枚举类型或字符串类型的变量单独定义到新的js文件中,通过 export const *** 记录,然后引用 const 名称使用。


使用actionCreator统一创建action

  • 优化前

    const value = this.state.inputValue
    const action = {
        type: 'change_child_value',
        value: value
    }
    store.dispatch(action);
    

  • 优化后

import { CHANGE_INPUT_VALUE } from './actionTypes';

export const getInputChangeAction = (value) => ({
    type: CHANGE_INPUT_VALUE,
    value
})
import {getInputChangeAction} from './actionCreator'
... ...
const action = getInputChangeAction(e.target.value);
store.dispatch(action)
... ...