Redux

Intro

Redux是一个数据层框架,方便组件之间的传值

r0

Store为公共存储区域,所有组件会自动感知到Store的变化

Redux = Reducer + Flux

工作流程

r1

设计原则

纯函数:固定输入,一定有固定输出(例如不允许date()函数等,以及Ajax请求),

并且无其他副作用(对参数不进行修改,深拷贝)

r6

核心API

r7

Demo

Antd

Antd为一个样式表,提供更好看的UI

yarn add antd

import ‘antd/dist/antd.css’;

Redux

yarn add redux

创建store文件夹,index.js文件

import { createStore } from 'redux';
import reducer from './reducer';

const store = createStore(reducer);

export default store;

reducer相当于笔记本,返回state

state表示store中的数据,可以设置为default

reducer.js

const defaultState = {
    inputValue: '',
    list: []
}

// reducer 可以接受state,但是绝不能修改state

export default (state = defaultState, action) => {
    return state;
}

使用store时,在相应的component中引入store

import store from ‘./store/‘

this.state = store.getState()

Redux调试

下载工具reduxdev,并在store参数中加入

const store = createStore(
    reducer, 
    window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
);

Action & Reducer

改变store中的数据,使用action

在component对应函数中,创建action

并将此action回传给store,通过dispatch

r2

r1

store会将当前值和action传给reducer,并结合进行操作,返回新的值

r3

深拷贝后return new state,并在store中做更新

在对应组件中做更新,只要store中的数据发生了改变,则执行对应函数

r4

在函数中刷新组件state,触发render

r5

reducer.js

import { INIT_LIST_ACTION, CHANGE_INPUT_VALUE, ADD_TODO_ITEM, DELETE_TODO_ITEM } from './actionTypes'

const defaultState = {
    inputValue: '',
    list: []
}

// reducer 可以接受state,但是绝不能修改state
// 纯函数指的是,给定固定的输入,就一定会有固定的输出,而且不会有任何副作用
export default (state = defaultState, action) => {

    if (action.type === CHANGE_INPUT_VALUE) {
        const newState = JSON.parse(JSON.stringify(state));
        newState.inputValue = action.value;
        return newState;
    }

    if (action.type === INIT_LIST_ACTION) {
        const newState = JSON.parse(JSON.stringify(state));
        newState.list = action.data;
        return newState;
    }

    if (action.type === ADD_TODO_ITEM) {
        const newState = JSON.parse(JSON.stringify(state));
        newState.list.push(newState.inputValue);
        newState.inputValue = '';
        return newState;
    }
    if (action.type === DELETE_TODO_ITEM) {
        const newState = JSON.parse(JSON.stringify(state));
        newState.list.splice(action.index, 1);
        return newState;
    }
    return state;
}

ActionType

actionType.js

export const CHANGE_INPUT_VALUE = 'change_input_value';
export const ADD_TODO_ITEM = 'add_todo_item';
export const DELETE_TODO_ITEM = 'delete_todo_item';
export const INIT_LIST_ACTION = 'init_list_action';

import { getInputChangeAction, getAddItemAction, getDeleteItemAction, initListAction } from ‘./store/actionCreators’

component和reducer中引入

并替换action中的type

actionCreator

避免直接定义action,使用creator统一创建,方便管理

actionCreator.js

import { CHANGE_INPUT_VALUE, ADD_TODO_ITEM, DELETE_TODO_ITEM, INIT_LIST_ACTION } from './actionTypes';

export const getInputChangeAction = (value) => ({
    type: CHANGE_INPUT_VALUE,
    value
});

export const getAddItemAction = () => ({
    type: ADD_TODO_ITEM
});

export const getDeleteItemAction = (index) => ({
    type: DELETE_TODO_ITEM,
    index
});

export const initListAction = (data) => ({
    type: INIT_LIST_ACTION,
    data
});

在component中引入

import { getInputChangeAction, getAddItemAction, getDeleteItemAction, initListAction } from ‘./store/actionCreators’

handleInputChange(e) {
        const action = getInputChangeAction(e.target.value);
        store.dispatch(action);
    }

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

    handleBtnClick() {
        const action = getAddItemAction();
        store.dispatch(action);
    }

    handleItemDelete(index) {
        const action = getDeleteItemAction(index);
        store.dispatch(action);
    }

UI与容器组件

将渲染与逻辑区分,UI组件负责渲染,容器组件负责逻辑

UI.js

import React, { Component } from 'react';
import { Input, Button, List } from 'antd';

class TodoListUI extends Component{
   render() {
    return (
        <div style={{marginTop: '10px', marginLeft: '10px'}}>
            <div>
                <Input 
                    value={this.props.inputValue} 
                    placeholder='todo info' 
                    style={{width: '300px', marginRight: '10px'}}
                    onChange={this.props.handleInputChange}
                />
                <Button type="primary" onClick={this.props.handleBtnClick}>提交</Button>
            </div>
            <List
                style={{marginTop: '10px', width: '300px'}}
          bordered
          dataSource={this.props.list}
          renderItem={(item, index) => (<List.Item onClick={(index) =>         {this.props.handleItemDelete(index)}}>{item}</List.Item>)}
        />
        </div>
    )
  } 
}
export default TodoListUI;

list.js

import React, { Component } from 'react';
import 'antd/dist/antd.css';
import store from './store';
import { getInputChangeAction, getAddItemAction, getDeleteItemAction, initListAction } from './store/actionCreators'
import TodoListUI from './TodoListUI';
import axios from 'axios';

class TodoList extends Component {

    constructor(props) {
        super(props);
        this.state = store.getState();
        this.handleInputChange = this.handleInputChange.bind(this);
        this.handleStoreChange = this.handleStoreChange.bind(this);
        this.handleBtnClick = this.handleBtnClick.bind(this);
        this.handleItemDelete = this.handleItemDelete.bind(this)
        store.subscribe(this.handleStoreChange);
    }

    render() {
        return (
            <TodoListUI 
                inputValue={this.state.inputValue}
                list={this.state.list}
                handleInputChange={this.handleInputChange}
                handleBtnClick={this.handleBtnClick}
                handleItemDelete={this.handleItemDelete}
            />
        )
    }

    componentDidMount() {
        axios.get('/list.json').then((res) => {
            const data = res.data;
            const action = initListAction(data);
            store.dispatch(action);
        })
    }

    handleInputChange(e) {
        const action = getInputChangeAction(e.target.value);
        store.dispatch(action);
    }

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

    handleBtnClick() {
        const action = getAddItemAction();
        store.dispatch(action);
    }

    handleItemDelete(index) {
        const action = getDeleteItemAction(index);
        store.dispatch(action);
    }

}

export default TodoList;

无状态组件

只有render函数的组件(相当于是一个函数),常为UI组件

把render return 改为一个函数,将this.props改为props

import React, { Component } from 'react';
import { Input, Button, List } from 'antd';

const TodoListUI = (props)=> {
    return (
        <div style={{marginTop: '10px', marginLeft: '10px'}}>
            <div>
                <Input 
                    value={props.inputValue} 
                    placeholder='todo info' 
                    style={{width: '300px', marginRight: '10px'}}
                    onChange={props.handleInputChange}
                />
                <Button type="primary" onClick={props.handleBtnClick}>提交</Button>
            </div>
            <List
                style={{marginTop: '10px', width: '300px'}}
          bordered
          dataSource={props.list}
          renderItem={(item, index) => (<List.Item onClick={(index) => {props.handleItemDelete(index)}}>{item}</List.Item>)}
        />
        </div>
    )
}

export default TodoListUI;

无需执行生命周期,无状态组件效率更高

Redux中异步请求数据

componentDidMount() {
        axios.get('/list.json').then((res) => {
            const data = res.data;
            const action = initListAction(data);
            store.dispatch(action);
        })
    }

actionCreator.js

export const initListAction = (data) => ({
    type: INIT_LIST_ACTION,
    data
});

React-Redux

引入provider

r8

provider将store提供给了内部的所有组件(todolist)

在todolist中引入connect和store,并且连接

r9

r10

传入两个参数,实现prop.function调用

this.props.changeInputValue

  • Copyrights © 2019-2020 Rex