React进阶(4)-拆分Redux-将store,Reducer,action,actionTypes独立管理

2020-10-28 16:36:13 浏览数 (1)

撰文 | 川川

前言

撰文:川川

在前面的几小节中已经完成了一个todolist的添加,删除的操作,通过把组件的数据放到了Redux中的公共存储区域store中去存储,在Redux中进行状态数据的更新修改

改变store的数据唯一办法就是派发action,调用store.dispatch方法,也知道通过getState方法获取store中的所有状态数据,而实现组件页面的更新与store保持同步,必须得触发注册subscribe方法,通时还得监听一个事件处理函数

用于重新在一次获取store的数据使页面同步更新

在上几次编写Redux的代码中,创建store,reducer,acton,以及actionTypes(动作类型)都是放在一个文件当中进行编写的,然而更改store可能有多个action动作,所有代码杂糅在一起,后期维护起来显然是非常痛苦的

所以有必要进行将Redux代码进行按照特定的职责,功能结构进行拆分的,其实也就是把之前各个逻辑代码拆分到各个文件当中去单独管理的

你将在本文中学习到

  • 如何拆分action,以及actionType,封装成一个函数放到actionCreator中去管理
  • 创建reducer,以及store
  • 抽离容器组件

· 正 · 文 · 来 · 啦 ·

完整的TodoList代码

这是上一节完整的一todolist的代码,创建store,reducer,以及action,UI组件等都是混写在一个文件当中的,这样虽然没有什么问题,但是维护起来,非常痛苦

如果一个文件里代码行数超过了130行,就应该考虑拆分代码了的,当然这并不是硬性的规定,适当的拆分有利于代码的维护,但是过度的拆分,也会增加项目的复杂程度

实际效果如下所示:

具体的实例代码如下所示

代码语言:javascript复制
import React from 'react';
import ReactDOM from 'react-dom';
import { Input, Button, List, message, Modal } from 'antd'; // 引入antd组件库
import 'antd/dist/antd.css'; // 引入antd样式


// 1. 创建一个store管理仓库,从redux库中引入一个createStore函数
import { createStore, applyMiddleware } from 'redux';
import { composeWithDevTools } from 'redux-devtools-extension';

// 2. 引入createStore后,store并没有创建,需要调用createStore()后才有store
//const store = createStore(reducer, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()); // 创建好reducer后,需要将reducer作为参数传到createStore当中去,这样store才能拿到reducer的state数据
const store = createStore(reducer, composeWithDevTools(applyMiddleware())); // 创建好reducer后,需要将reducer作为参数传到createStore当中去,这样store才能拿到reducer的state数据

// 3. 创建reducer函数,管理组件共享的数据状态以及一些动作
// reducer是一个纯函数,返回一个新的state给store
// 4. 初始化state值,将原先组件内部的状态的数据,移除到reducer里面去管理
function reducer(state = {
    inputValue: '',
    list: []
}, action) {
    console.log(state, action);
    if (action.type === 'handle_Input_Change') {
        // 对原有的上一次的state做一次深拷贝,在Redux中,reducer不允许直接修改state
        // const newState = Object.assign({}, state);
        const newState = JSON.parse(JSON.stringify(state));
        newState.inputValue = action.value; // 将新的value值赋值给newState
        return newState;
    }
    if (action.type === 'addInputcontent') {
        const newState = JSON.parse(JSON.stringify(state));
        if (Trim(newState.inputValue) === '') {
            message.error('输入表单内不能为空,请输入内容');
        } else {
            newState.list.push(newState.inputValue); // 往list数组中添加input的内容
            newState.inputValue = '';
            return newState; // 返回newState
        }

    }
    if (action.type === 'deletelist') {
        // 下面这个也是拷贝原对象的一种方式与上面等价
        const newState = Object.assign({}, state);
        newState.list.splice(action.index, 1);
        return newState;
    }
    return state;
}

// 去除前后空格
function Trim(str) {
    return str.replace(/(^s*)|(s*$)/g, "");
}
const { confirm } = Modal
// TodoList组件
class TodoList extends React.Component {

    constructor(props) {
        super(props);
        // 5. 在组件内部通过getState()方法就可以拿到store里面的数据
        this.state = store.getState();
        // this环境的绑定
        this.handleInputChange = this.handleInputChange.bind(this);
        this.handleStoreChange = this.handleStoreChange.bind(this);
        this.handleAddClick = this.handleAddClick.bind(this);
        // 触发订阅,让store感知到state的变化
        store.subscribe(this.handleStoreChange); // 接收一个函数,重新获取store最新的数据,subscribe里面必须接收一个函数,否则是会报错的,这个订阅函数放在componentWillMount生命周期函数内调用操作也是可以的
    }
    // componentWillMount(){
    // store.subscribe(this.handleStoreChange);
    // }
    // 组件卸载,移除时调用该函数,一般取消,清理已注册的订阅,定时器的清理,取消网络请求,在这里面操作
    componentWillUnmount() {
        store.unsubscribe(this.handleStoreChange);
    }

    render() {
        return (
            <div style={{width:'600px',margin: "100px auto"}}>
                    <div>
                        <Input onChange={this.handleInputChange} value={this.state.inputValue} style={{ width:"300px",marginRight:"10px"}} placeholder="请输入内容..." />
                        <Button type="primary" onClick={this.handleAddClick}>提交</Button>
                    </div>
                    <List
                      style={{ width: '300px',marginTop:'10px'}}
                      bordered
                      dataSource={this.state.list}
                      renderItem={(item,index) => <List.Item onClick={this.handleDelList.bind(this, index,item)}>{item}</List.Item>}/>
            </div>
        )
    }

    handleInputChange(e) {
        console.log(e.target.value);
        // 定义action,确定一个操作,动作,注意action必须遵循一定的规范,是一个对象,type字段是确定要做的动作,类型,监听表单输入框的变化,value是输入框的值
        const action = {
            type: 'handle_Input_Change',
            value: e.target.value
        }
        store.dispatch(action); // 通过store派发dispatch一个action,只有这里接收一个action,Reducer里面才能对新旧数据进行计算等操作

    }

    handleStoreChange() {
        console.log("handleStorechange,触发了");
        this.setState(store.getState()); // 触发setState重新获取store的数据,让input的数据与store保持同步了的
    }

    // 添加列表的操作
    handleAddClick() {
        console.log("添加按钮执行了");
        // 定义action动作
        const action = {
            type: 'addInputcontent'
        }
        store.dispatch(action); // 还要将action传递给dispatch,这样store才会接收到
    }

    // 删除列表操作
    handleDelList(index,item) {
        this.showDeleteConfirm(index, item);
    }

    showDeleteConfirm(index,item) {
        const action = {
            type: 'deletelist',
            index: index
        }
        confirm({
            title: '确定要删除该列表?',
            content: item,
            okText: '确认',
            okType: 'danger',
            cancelText: '取消',
            onOk() {
                console.log('OK');
                 store.dispatch(action);
            },
            onCancel() {
                console.log('Cancel');
            },
        });
    }
}
const container = document.getElementById('root');

ReactDOM.render(<TodoList />, container);

此时,项目的src根目下只有一个index.js文件,项目的目录树结构是这样的

代码语言:javascript复制
D:公开课2019React进阶lesson2
├─split-redux
| ├─.gitignore
| ├─package-lock.json
| ├─package.json
| ├─README.md
| ├─yarn-error.log
| ├─yarn.lock
| ├─src
| | ├─index.js // Redux以及组件的业务逻辑都在一个文件里面
| ├─public
| | ├─favicon.ico
| | ├─index.html
| | └manifest.json

下面来一步一步拆分的,先从简单的入手,不断的简化代码的

(专用拖把洗车,自给自足,省事,简单)

拆分ActionTypes定义成一个常量,独立管理

改变store里面state数据,唯一的办法就是派发action,调用store.dispatch(action)方法

而定义action,它得是一个对象,该对象下type类型必须是一个字符串类型值,这个类型值必须和reducer里面action.type后面的值相同,如果不相等,控制台虽然不报错,但是却会影响实际的功能

代码如下所示

代码语言:javascript复制
// 定义action,也就是具体要做的什么事情
const action = {
      type: 'handle_Input_Change', // 这个type后面的字符串值与在reducer里面的action.type相同
      value: e.target.value
}
// 字符串类型值要与reducer相同
function reducer(state, action){
    if (action.type === 'handle_Input_Change') { // 这个必须要与上面定义相同
        // 对原有的上一次的state做一次深拷贝,在Redux中,reducer不允许直接修改state
        // const newState = Object.assign({}, state);
        const newState = JSON.parse(JSON.stringify(state));
        newState.inputValue = action.value; // 将新的value值赋值给newState
        return newState;
    }
}

在根目录src下创建一个store文件夹,然后在新建一个actionsTypes.js

把上面action对象下的type的类型值定义成一个常量,然后对外暴露出去,因为这个动作type类型往往是固定的,一般不怎么去改变,类型值与常量名都定义成同名,这里的类型值与常量名设置成同名不一定非要一致,但是这已经是大家约定俗成的一种规定,是个良好的开发习惯

定义actionType类型如下所示,将action的type类型值定义成常量

代码语言:javascript复制
const CHANGE_INPUT_VALUE = 'CHANGE_INPUT_VALUE'; // 字符串值是小写也是可以的

export {
    CHANGE_INPUT_VALUE
}

然后在需要使用actionType类型处,引入该暴露的变量对象即可

代码语言:javascript复制
import { CHANGE_INPUT_VALUE } from './store/actionTypes'; // 引入actionTypes类型

handleInputChange(e) {
        const action = {
            type: CHANGE_INPUT_VALUE, // 这里引入上面定义的变量对象
            value: e.target.value
        }
        store.dispatch(action); // 通过store派发dispatch一个action,只有这里接收一个action,Reducer里面才能对新旧数据进行计算等操作

    }

以此类推,按照以上模式把action里面的type类型值都更改成常量,放到一个文件(actionTypes.js)去管理的,这个文件只用于定义动作action类型的常量

因为上面的代码中的action有三个:所以完整的如下所示

代码语言:javascript复制
const CHANGE_INPUT_VALUE = 'CHANGE_INPUT_VALUE'; // 监听input框输入值的常量
const ADD_INPUT_CONTENT = 'ADD_INPUT_CONTENT'; // 添加列表
const DELETE_LIST = 'DELETE_LIST'; // 删除列表


export {
    CHANGE_INPUT_VALUE,
    ADD_INPUT_CONTENT,
    DELETE_LIST
}

然后在需要使用action Type的地方引用即可

代码语言:javascript复制
import { CHANGE_INPUT_VALUE, ADD_INPUT_CONTENT, DELETE_LIST } from './store/actionTypes'; // 引入actionTypes
// 监听input变化动作
handleInputChange(e) {
        const action = {
            type: CHANGE_INPUT_VALUE,
            value: e.target.value
        }
        store.dispatch(action);

}

// 添加列表的操作
handleAddClick() {
    // 定义action动作
    const action = {
        type: ADD_INPUT_CONTENT
    }
    store.dispatch(action); // 还要将action传递给dispatch,这样store才会接收到
}

// 删除列表操作
    handleDelList(index,item) {
        this.showDeleteConfirm(index, item);
    }

    showDeleteConfirm(index,item) {
        const action = { // action在这里
            type: DELETE_LIST,
            index: index
        }
        confirm({
            title: '确定要删除该列表?',
            content: item,
            okText: '确认',
            okType: 'danger',
            cancelText: '取消',
            onOk() {
                console.log('OK');
                 store.dispatch(action);
            },
            onCancel() {
                console.log('Cancel');
            },
        });
    }

对于上面引入actionTypes,其实还有一种更简便的方法,actionTypes比较多的话,以下这种方式是比较方便的,通过*号一次性导入,起一个别名,然后通过对象的方式引入使用,这种方式在代码中也是比较常见的

代码语言:javascript复制
import * as constants from './store/actionTypes'; // 通过*号全部引入,起一个变量名,引入actionTypes
// 监听input变化动作
handleInputChange(e) {
        const action = {
            type: constants.CHANGE_INPUT_VALUE,
            value: e.target.value
        }
        store.dispatch(action);

}

// 添加列表的操作
handleAddClick() {
    // 定义action动作
    const action = {
        type: constants.ADD_INPUT_CONTENT
    }
    store.dispatch(action); // 还要将action传递给dispatch,这样store才会接收到
}

// 删除列表操作
    handleDelList(index,item) {
        this.showDeleteConfirm(index, item);
    }

    showDeleteConfirm(index,item) {
        const action = { // action在这里
            type: constants.DELETE_LIST,
            index: index
        }
        confirm({
            title: '确定要删除该列表?',
            content: item,
            okText: '确认',
            okType: 'danger',
            cancelText: '取消',
            onOk() {
                console.log('OK');
                 store.dispatch(action);
            },
            onCancel() {
                console.log('Cancel');
            },
        });
    }

经过上面的处理,关于action的type类型值就已经拆分出去了的,至于拆分action中type类型值的好处就是,当你因为不小心把actionType拼写错误时,它会有很好的错误异常提示,这就是定义成一个常量的好处

拆分action,将它封装到一个函数里面去管理

在上面的代码中,只是把action中的type类型值定义成一个常量然后拆分了出去的,但是仍然发现,代码并没有简化多少

其实在派发action之前,改变store的数据,对于action的动作(具体要做的事情),是不应该直接定义在我们的组件里,在事件处理函数里面定义action对象不是不可以。

但是这样代码的内聚性不高,对于简易的项目,一些action定义在各个组件内,也没有什么,但是一多的话,找起来就是灾难了的,不利于后续代码的维护

如果你能够把相应的action代码拆分出去,后来的同学一定会感谢你的,当然随之而然就是增加了点阅读代码的复杂度

如果你是高手,那绝对从内心上是要感谢那种把action拆分到一个文件里去管理的,至于初学者,那肯定觉得特么复杂的,非常绕以及难以理解的,其实只要把Redux的工作流程图理清楚了,也就自然为什么要这么拆分了的

通常来说,我们把上面的action都放在一个action Creators.js的文件中去管理的,管理这个action文件代码的名字并不是固定的,你想要怎么定义成管理action的任何一个名字都可以,但是最好是见名知意

具体actionCreators.js代码如下所示:

代码语言:javascript复制
import { CHANGE_INPUT_VALUE, ADD_INPUT_CONTENT, DELETE_LIST } from './actionTypes'; // 引入actionTypes

// 将action封装成一个函数,用于返回type类型和需要的参数
function getInputChangeAction(value){
    return {
        type: CHANGE_INPUT_VALUE,
        value:value
    }
}

// 获取input框内容,添加列表action函数
function getAddInputContentAction(){
    return {
        type: ADD_INPUT_CONTENT
    }
}

// 获取删除列表acton函数
function getDeleteListAction(index){
    return {
        type: DELETE_LIST,
        index:index
    }
}

// 上面的也等价于,在Es6中有简写函数的形式,与下面是等价的,在React代码中这种写法很常见
/*
const getInputChangeAction = (value) => ({
    type: CHANGE_INPUT_VALUE,
    value
});

const getAddInputContentAction = () => ({
    type: ADD_INPUT_CONTENT
})

const getDeleteListAction = index => ({ // 当只有一个参数时,圆括号可以省略,当返回值有多个时,外面需要用一个大括号包裹起来的
    type: DELETE_LIST,
    index
})

*/
// 将变量对象的函数给暴露出去
export {
    getInputChangeAction,
    getAddInputContentAction,
    getDeleteListAction
}

在组件所需要引入actionCreactors的地方,引入actions,如下所示:

代码语言:javascript复制
import { getInputChangeAction, getAddInputContentAction, getDeleteListAction} from './store/actionCreators';


// 监听input操作
 handleInputChange(e) {
        const action = getInputChangeAction(e.target.value);
        store.dispatch(action);
 }
 
 // 添加操作
 handleAddClick() {
    const action = getAddInputContentAction();
    store.dispatch(action);
 }
 
 // 删除列表操作
    handleDelList(index,item) {
        this.showDeleteConfirm(index, item);
    }

    showDeleteConfirm(index,item) {
        const action = getDeleteListAction(index);
        confirm({
            title: '确定要删除该列表?',
            content: item,
            okText: '确认',
            okType: 'danger',
            cancelText: '取消',
            onOk() {
                console.log('OK');
                 store.dispatch(action);
            },
            onCancel() {
                console.log('Cancel');
            },
        });
    }

经过上面的action的拆分,现在看来我们的代码清晰多了,通过actionCreators来创建action,这是一个非常好的编程习惯,当然如果过度的拆分,就难免会让人觉得项目复杂,在各个文件之间来回切来切去的,如果不清晰他们之间的关系,那么的确是比较绕,但是不能因为这样,就不做拆分的

从长远来看,拆分action是很有必要的,一是将事件动作的类型定义成常量给分离出去,二是把整体action单独封装成一个函数放在一个单独的文件中进行管理的,它返回对应的类型和必要的参数的

拆分的目的主要是提高代码的可维护性

(解释下单页面应用,一个页面,主要体现在入口上)

创建store单独管理

在上面的代码中,已经解决了Redux工作流程中的右半边部分,也就是做了action的拆分管理,那么接下来是整理store和reducer以及React Component了

在store文件夹中创建一个index.js的文件

这个index.js主要用于创建store

代码语言:javascript复制
import { createStore } from "redux";
// 创建store,调用createStore函数
const store = createStore();

export default store;

创建reducer,更新state数据操作

在store文件夹下创建reducer.js文件,主要用于更新state数据操作,如下代码所示

代码语言:javascript复制
import { message } from 'antd';
import { CHANGE_INPUT_VALUE, ADD_INPUT_CONTENT, DELETE_LIST } from './actionTypes';
const defaultStatus = { // 默认初始值
    inputValue: 'itclanCoder',
    list: ['川川','111', '222']
}
function reducer(state=defaultStatus, action){
    if(action.type === CHANGE_INPUT_VALUE){
        const newState = JSON.parse(JSON.stringify(state));
        newState.inputValue = action.value;
        return newState;
    }

    if(action.type === ADD_INPUT_CONTENT){
        const newState = JSON.parse(JSON.stringify(state));
        if (Trim(newState.inputValue) === '') {
            message.error('输入表单内不能为空,请输入内容');
        } else {
            newState.list.push(newState.inputValue); // 往list数组中添加input的内容
            newState.inputValue = '';
            return newState; // 返回newState
        }
    }

    if(action.type === DELETE_LIST){
        const newState = JSON.parse(JSON.stringify(state));
        newState.list.splice(action.index, 1);
        return newState;
    }
    return state;
}


// 去除前后空格
function Trim(str) {
    return str.replace(/(^s*)|(s*$)/g, "");
}

export default reducer;

在创建好reducer后,一定把reducer放到createStore()函数当做参数给传进去,这样store才会真正存储reducer的数据,同时把store给暴露出去,如下store文件夹中index.js的代码

代码语言:javascript复制
import { createStore, applyMiddleware } from "redux";
import { composeWithDevTools } from 'redux-devtools-extension'; // 这个是redux-devtools调试工具
import reducer from './reducer'; // 引入reducer
// 创建store
const store = createStore(reducer, composeWithDevTools(applyMiddleware()));

export default store; // 导出store

最后在主入口文件index.js中引入store,全局进行使用的,如下代码所示

代码语言:javascript复制
import React from 'react';
import ReactDOM from 'react-dom';
import { Input, Button, List, Modal } from 'antd'; // 引入antd组件库
import 'antd/dist/antd.css'; // 引入antd样式
import { getInputChangeAction, getAddInputContentAction, getDeleteListAction} from './store/actionCreators';
import store from './store/'; // 引入store


const { confirm } = Modal
// TodoList组件
class TodoList extends React.Component {

    constructor(props) {
        super(props);
        // 5. 在组件内部通过getState()方法就可以拿到store里面的数据
        this.state = store.getState();
        // this环境的绑定
        this.handleInputChange = this.handleInputChange.bind(this);
        this.handleStoreChange = this.handleStoreChange.bind(this);
        this.handleAddClick = this.handleAddClick.bind(this);
        // 触发订阅,让store感知到state的变化
        store.subscribe(this.handleStoreChange); // 接收一个函数,重新获取store最新的数据,subscribe里面必须接收一个函数,否则是会报错的,这个订阅函数放在componentWillMount生命周期函数内调用操作也是可以的
    }
    // componentWillMount(){
    // store.subscribe(this.handleStoreChange);
    // }
    // 组件卸载,移除时调用该函数,一般取消,清理已注册的订阅,定时器的清理,取消网络请求,在这里面操作
    componentWillUnmount() {
        store.unsubscribe(this.handleStoreChange);
    }

    render() {
        return (
            <div style={{width:'600px',margin: "100px auto"}}>
                    <div>
                        <Input onChange={this.handleInputChange} value={this.state.inputValue} style={{ width:"300px",marginRight:"10px"}} placeholder="请输入内容..." />
                        <Button type="primary" onClick={this.handleAddClick}>提交</Button>
                    </div>
                    <List
                      style={{ width: '300px',marginTop:'10px'}}
                      bordered
                      dataSource={this.state.list}
                      renderItem={(item,index) => <List.Item onClick={this.handleDelList.bind(this, index,item)}>{item}</List.Item>}/>
            </div>
        )
    }

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

    handleStoreChange() {
        console.log("handleStorechange,触发了");
        this.setState(store.getState()); // 触发setState重新获取store的数据,让input的数据与store保持同步了的
    }

    // 添加列表的操作
    handleAddClick() {
        const action = getAddInputContentAction();
        store.dispatch(action);
    }

    // 删除列表操作
    handleDelList(index,item) {
        this.showDeleteConfirm(index, item);
    }

    showDeleteConfirm(index,item) {
        const action = getDeleteListAction(index);
        confirm({
            title: '确定要删除该列表?',
            content: item,
            okText: '确认',
            okType: 'danger',
            cancelText: '取消',
            onOk() {
                console.log('OK');
                 store.dispatch(action);
            },
            onCancel() {
                console.log('Cancel');
            },
        });
    }
}
const container = document.getElementById('root');

ReactDOM.render(<TodoList />, container);

上面的代码是渲染一个todolist组件的功能,显然对于主入口文件,我们仍希望它是比较干净的

我们继续将todolist组件单独的抽离出去的

抽离容器组件

对于todolist就是一个简单的组件,那么我们可以把它抽离出去单独定义的,在根目录src下创建一个views文件夹,这个文件夹可以放我们的视图组件,在里面建一个TodoList.js的文件的 具体代码如下所示:

对于下面用类class定义声明的TodoList组件,称作为一个容器组件,之所以这么叫,是因为在这个组件里面包含很多业务逻辑,例如:this坏境的绑定,生命周期函数,以及一些事件处理函数等,负责整个业务功能组件的逻辑实现,也有人叫它聪明组件的,这个只是个称呼而已,没有褒贬之义 如下代码所示

代码语言:javascript复制
import React from 'react';
import { Input, Button, List, Modal } from 'antd'; // 引入antd组件库
import 'antd/dist/antd.css'; // 引入antd样式
import { getInputChangeAction, getAddInputContentAction, getDeleteListAction} from '../store/actionCreators';
import store from '../store/index'; // 引入store


const { confirm } = Modal
// TodoList组件
class TodoList extends React.Component {

    constructor(props) {
        super(props);
        // 5. 在组件内部通过getState()方法就可以拿到store里面的数据
        this.state = store.getState();
        // this环境的绑定
        this.handleInputChange = this.handleInputChange.bind(this);
        this.handleStoreChange = this.handleStoreChange.bind(this);
        this.handleAddClick = this.handleAddClick.bind(this);
        // 触发订阅,让store感知到state的变化
        store.subscribe(this.handleStoreChange); // 接收一个函数,重新获取store最新的数据,subscribe里面必须接收一个函数,否则是会报错的,这个订阅函数放在componentWillMount生命周期函数内调用操作也是可以的
    }
    // componentWillMount(){
    // store.subscribe(this.handleStoreChange);
    // }
    // 组件卸载,移除时调用该函数,一般取消,清理已注册的订阅,定时器的清理,取消网络请求,在这里面操作
    componentWillUnmount() {
        store.unsubscribe(this.handleStoreChange);
    }

    render() {
        return (
            <div style={{width:'600px',margin: "100px auto"}}>
                    <div>
                        <Input onChange={this.handleInputChange} value={this.state.inputValue} style={{ width:"300px",marginRight:"10px"}} placeholder="请输入内容..." />
                        <Button type="primary" onClick={this.handleAddClick}>提交</Button>
                    </div>
                    <List
                      style={{ width: '300px',marginTop:'10px'}}
                      bordered
                      dataSource={this.state.list}
                      renderItem={(item,index) => <List.Item onClick={this.handleDelList.bind(this, index,item)}>{item}</List.Item>}/>
            </div>
        )
    }

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

    handleStoreChange() {
        console.log("handleStorechange,触发了");
        this.setState(store.getState()); // 触发setState重新获取store的数据,让input的数据与store保持同步了的
    }

    // 添加列表的操作
    handleAddClick() {
        const action = getAddInputContentAction();
        store.dispatch(action);
    }

    // 删除列表操作
    handleDelList(index,item) {
        this.showDeleteConfirm(index, item);
    }

    showDeleteConfirm(index,item) {
        const action = getDeleteListAction(index);
        confirm({
            title: '确定要删除该列表?',
            content: item,
            okText: '确认',
            okType: 'danger',
            cancelText: '取消',
            onOk() {
                console.log('OK');
                 store.dispatch(action);
            },
            onCancel() {
                console.log('Cancel');
            },
        });
    }
}

export default TodoList;

其实没有做多大的代码改变,只是把原先的代码挪到另一个文件管理了的,那么现在的项目目录结构是这样的

代码语言:javascript复制
D:公开课2019React进阶lesson2
├─split-redux
| ├─.gitignore
| ├─package-lock.json
| ├─package.json
| ├─README.md
| ├─yarn-error.log
| ├─yarn.lock
| ├─src
| | ├─index.js // 主入口文件
| | ├─views
| | | └TodoList.js // 容器组件
| | ├─store // 组件的数据
| | | ├─actionCreators.js // action创建者
| | | ├─actionTypes.js // actionType的类型,定义成的常量
| | | ├─index.js // 创建的store主文件
| | | └reducer.js // 创建的reducer
| ├─public
| | ├─favicon.ico
| | ├─index.html
| | └manifest.json

从这个目录树中,非常清楚了的,由起初在index.js的代码,把redux中的store,reducer,action逐渐剥离出去单独管理了的

结语

本小节主要是对上一节代码的拆分,将Redux中的store,action,以及reducer分离开来,各自独立的管理,职责分明,如果项目比较简单,一开始是可以写在一块的,然后一点的拆分出去的

如果不是老司机,一开始一上来就拆分,如果对Redux的工作流程不是很清晰,难免会有所懵逼,发现,写着,写着,找不到头绪,不知道自己在写些什么的

在实际开发当中,至于拆分的顺序,不一定按照我这种方式的,合适的流程应当时,先创建store,然后在创建reducer,确定要做什么事情,编写action,拆分action代码,其中获取store就用getState方法,而更改store就要通过dispatch派发action,这个流程是固定的

要理解Store是用来存储组件的公共数据状态的,它就是一个把Reducer关联到一起的一个对象,而Reducer就是根据Action发出的type(动作类型)来做某些事情

当然这个代码仍然优化的地方,我们在后续当中,仍会进一步的拆分的

0 人点赞