React Redux(三)

文章详细介绍了Redux中间件的作用,如redux-logger和redux-thunk、redux-promise,用于处理异步操作和日志记录。通过示例展示了如何使用这些中间件进行派发操作,特别是如何在不直接支持异步的actionCreator中实现异步功能。最后,文章提到了在实际项目中如何利用Redux进行数据管理,减少服务器请求。
摘要由CSDN通过智能技术生成

redux中间件及其处理机制

redux中文网API文档之redux中间件:https://cn.redux.js.org/api/applymiddleware

redux中间件作用示意图:
在这里插入图片描述
中间件使用:

在这里插入图片描述

redux-logger:

import { createStore, applyMiddleware } from 'redux';
import reducer from './reducers';
import reduxLogger from 'redux-logger'

const store = createStore(
    reducer,
    applyMiddleware(reduxLogger)
);

export default store;

在这里插入图片描述
redux-thunk/redux-promise语法及原理:
延迟函数:返回promise示例,在指定的时间后,才会让实例为成功

const delay = (interval = 1000) => {
    return new Promise((resolve) => {
        setTimeout(() => {
            resolve()
        }, interval)
    })
}
let fn = async () => {
    await delay()
    return { x: 200 }
}
//把fn()执行,立即拿到的结果并不是{ x: 200 ],而是一个promise实例
console.log(fn());// Promise {<pending>}
fn().then(value => {
    console.log(value);
})

在这里插入图片描述
如果直接将延迟函数应用于行为对象派发中,是有问题的:

const voteAction = {
    async support() {
        await delay()
        return {
            type: TYPES.VOTE_SUP
        };
    },
    ...
}

在这里插入图片描述
原因:
在这里插入图片描述
在不使用任何中间件的情况下,actionCreator对象中,是不支持异步操作的;我们要保证方法执行,要必须立即返回标准的action对象,类似于下述这种:

    support() {
        return {
            type: TYPES.VOTE_SUP
        };
    },

但真实项目中,往往我们是真的需要异步操作的,例如在派发的时候,我们需要先向服务器发送请求,把数据拿到后再进行派发。此时我们需要依托于一些中间件来进行处理。

官方推荐:redux-thunk
在这里插入图片描述

const voteAction = {
    //redux-thunk中间件的语法
    support() {
        return async (dispatch) => {
            await delay()
            dispatch({
                type: TYPES.VOTE_SUP
            });
        }
    },
    ...
}

原理:

在这里插入图片描述
发现问题:
在这里插入图片描述
在这里插入图片描述
原理改进理解:

在这里插入图片描述
在这里插入图片描述
也可以基于redux-promise中间件,完成异步的派发:
在这里插入图片描述

const voteAction = {
    ...
    //redux-promise中间件的语法
    async oppose() {
        await delay(2000)
        return {
            type: TYPES.VOTE_OPP
        };
    }
};

原理:

在这里插入图片描述
完整的操作代码:
store文件夹下的index.js:

import { createStore, applyMiddleware } from 'redux';
import reducer from './reducers';
import reduxLogger from 'redux-logger'
import reduxThunk from 'redux-thunk'
import reduxPromise from 'redux-promise'

const store = createStore(
    reducer,
    applyMiddleware(reduxLogger, reduxThunk, reduxPromise)
);
export default store;

VoteAction.js:

import * as TYPES from '../action-types';
//延迟函数:返回promise实例,在指定的时间后,才会让实例成功
const delay = (interval = 1000) => {
    return new Promise(resolve => {
        setTimeout(() => {
            resolve()
        }, interval)
    })
}
const voteAction = {
    //redux-thunk中间件的语法
    support() {
        return async (dispatch) => {
            await delay()
            dispatch({
                type: TYPES.VOTE_SUP
            });
        }
    },
    //redux-promise中间件的语法
    async oppose() {
        await delay(2000)
        return {
            type: TYPES.VOTE_OPP
        };
    }
};
export default voteAction;

总结:redux-promise和redux-thunk中间件,都是处理异步派发的
他们相同的地方:都是派发两次

  • 第一次派发用的都是重写后的dispatch,这个方法不会去校验对象是否有type属性,也不会在乎传递的对象是否为标准普通对象
  • 此次派发,仅仅是为了第二次派发做准备
    • redux-thunk:把返回的函数执行,把真正的dispath传递给函数
    • redux-promise:监听返回的promise实例,在实例为成功后,需要基于真正的disptch把成功的结果再进行派发

区别:
redux-thunk的第二次派发是手动处理的;而redux-promise的第二次派发是自动处理的。

基于redux重构TASK OA

redux在真实项目中的运用:

  1. 实现组件之间的信息共享或者通信
  2. 对数据进行缓存/存储 => 减少向服务器发送请求的次数

在这里插入图片描述

redux工程化

在这里插入图片描述
store/action-types.js:

export const TASK_LIST = 'TASK_LIST'
export const TASK_REMOVE = 'TASK_REMOVE'
export const TASK_UPDATE = 'TASK_UPDATE'

在这里插入图片描述

获取当前时间:
new Date().toLocaleString(‘zh-CN’, { hour12: false })

store/reducers/taskReducer.js:

import * as TYPES from '../action-types'
import _ from '../../assets/utils'
const initial = {
    taskList: null
}
export default function taskReducer(state = initial, action) {
    state = _.clone(true, state)
    let { taskList } = state
    switch (action.types) {
        case TYPES.TASK_LIST:
            state.taskList = action.list
            break
        case TYPES.TASK_DELETE:
            if (Array.isArray(taskList)) {
                state.taskList = taskList.filter(item => {
                    return +item.id !== +action.id
                })
            }
            break
        case TYPES.TASK_UPDATE:
            if (Array.isArray(taskList)) {
                state.taskList = taskList.map(item => {
                    if (+item.id === +action.id) {
                        item.state = 2
                        item.complete = new Date().toLocaleString('zh-CN', { hour12: false })
                    }
                    return item
                })
            }
            break
        default:
    }
    return state
}

// 获取全部任务 => dispath({type:'TASK_LIST',list:[...]})
// 删除任务 => dispath({type:'TASK_DELETE',id:xxx})
// 完成任务 => dispath({type:'TASK_UPDATE',id:xxx})

store/reducers/index.js:

import { combineReducers } from "redux";
import taskReducer from "./taskReducer";

const reducer = combineReducers({
    task: taskReducer
})
export default reducer

store/actions/taskAction.js:

import * as TYPES from '../action-types'
import { getTaskList } from '../../api'
const taskAction = {
    // 异步派发:从服务器获取全部任务,同步到redux容器中
    async queryAllList() {
        let list = []
        try {
            let result = await getTaskList()
            if (+result.code === 0) {
                list = result.list
            }
        } catch (_) { }
        return {
            type: TYPES.TASK_LIST,
            list
        }
    },
    // 同步派发:删除执行任务
    deleteTaskById(id) {
        return {
            type: TYPES.TASK_DELETE,
            id
        }
    },
    // 同步派发:修改任务状态
    updateTaskById(id) {
        return {
            type: TYPES.TASK_UPDATE,
            id
        }
    }
}
export default taskAction

store/actions/index.js:

import taskAction from "./taskAction";

const action = {
    task: taskAction
}
export default action

store/index.js:

import { createStore, applyMiddleware } from 'redux'
import reducer from './reducers/index'
import reduxLogger from 'redux-logger'
import reduxPromise from 'redux-promise'
import reduxThunk from 'redux-thunk'

const store = createStore(
    reducer,
    applyMiddleware(reduxLogger, reduxPromise, reduxThunk)
)
export default store
在组件中,基于react-redux实现需求
index.js:
...
/* 使用REDUX */
import store from './store/index'
import { Provider } from 'react-redux'

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
    <ConfigProvider locale={zhCN}>
        <Provider store={store}>
            <Task />
        </Provider>
    </ConfigProvider>
);
Task.jsx

从react-redux中解构出connect函数,将所需要的状态和派发行为对象的方法作为属性传递给要渲染的组件,在组件中获取基于属性传递进来的公共状态和ActionCreator:

import { connect } from 'react-redux'
import action from '@/store/actions/index.js'
...
const Task = function Task(props) {
    let { taskList, qeuryAllList, deleteTaskById, updateTaskById } = props
...
}
export default connect(
    state => state.task,
    action.task
)(Task);

关于Table和数据的处理:
第一次渲染完毕,判断redux中是否有全部的任务;如果没有,则进行异步的派发

    useEffect(() => {
        if (!taskList) {
            queryAllList()
        }
    }, [])

依赖于redux中的全部任务 & 根据选中的状态信息,从全部任务中筛选出表格需要的数据

    useEffect(() => {
        if (!taskList) {
            setTableData([])
            return
        }
        if (selectedIndex !== 0) {
            taskList = taskList.filter(item => {
                return +item.state === +selectedIndex
            })
        }
        setTableData(taskList)
    }, [taskList, selectedIndex])

submit函数中,每次添加新任务后进行任务派发,重新获取全部任务信息,同步到redux中

    const submit = async () => {
        try {
            await formIns.validateFields()
            let { task, time } = formIns.getFieldsValue()
            time = time.format('YYYY-MM-DD HH:mm:ss')
            setConfirmLoading(true)
            let { code } = await addTask(task, time)
            if (+code === 0) {
                closeModal()
                queryAllList()
                message.success("恭喜添加成功")
            } else {
                message.error("很遗憾添加失败")
            }
        } catch (_) { }
        setConfirmLoading(false)
    }

删除和更新任务状态的函数removeHandle和updateHandle中,每次通知服务器进行相关操作后不必再从服务器中重新获取数据(task列表),而是进行相应任务派发,修改redux容器中公共状态信息,从而减少应用向服务器请求次数

    const removeHandle = async (id) => {
        try {
            let { code } = await removeTask(id)
            if (+code === 0) {
                deleteTaskById(id)// 派发任务,删除redux中的数据
                message.success("恭喜删除成功")
            } else {
                message.error("很遗憾删除失败")
            }
        } catch (_) { }
    }
    const updateHandle = async (id) => {
        try {
            let { code } = await completeTask(id)
            if (+code === 0) {
                updateTaskById(id)// 派发任务,修改redux中的数据
                message.success("恭喜更新任务状态成功")
            } else {
                message.error("很遗憾更新失败")
            }
        } catch (_) { }
    }

优化: 第一次渲染组件获取数据时的loading效果

    useEffect(() => {
        (async () => {
            if (!taskList) {
                setTableLoading(true)
                await queryAllList()
                setTableLoading(false)
            }
        })()
    }, [])

Task.jsx完整代码:

import React, { useState, useEffect } from "react";
import './Task.less';
import { Button, Tag, Table, Popconfirm, Modal, Form, Input, DatePicker, Divider, message } from 'antd'
import { getTaskList, addTask, removeTask, completeTask } from '../api'
import { connect } from 'react-redux'
import action from '@/store/actions/index.js'

// 对日期的处理方法
const zero = function zero(text) {
    text = String(text)
    return text.length < 2 ? '0' + text : text
}
const formatTime = function formatTime(time) {
    let arr = time.match(/\d+/g),
        [_, month, day, hour = "00", minute = "00"] = arr
    return `${zero(month)}-${zero(day)} ${zero(hour)}:${zero(minute)}`
}
const Task = function Task(props) {
    /* 获取基于属性传递进来的公共状态 & ActionCreator */
    let { taskList, queryAllList, deleteTaskById, updateTaskById } = props
    /* 表格列的数据 */
    const columns = [
        {
            title: '编号',
            dataIndex: 'id',
            align: 'center',
            width: '8%',
        },
        {
            title: '任务描述',
            dataIndex: 'task',
            ellipsis: true,
            width: '50%',
        },
        {
            title: '状态',
            dataIndex: 'state',
            align: 'center',
            width: '10%',
            render: text => +text === 1 ? '未完成' : '已完成'
        },
        {
            title: '完成时间',
            dataIndex: 'time',
            align: 'center',
            width: '15%',
            render: (_, record) => {
                let { state, time, complete } = record
                if (+state === 2) time = complete
                return formatTime(time)
            }
        },
        {
            title: '操作',
            dataIndex: 'complete',
            render: (_, record) => {
                let { id, state } = record
                return <>
                    <Popconfirm
                        title="您确定要删除此任务吗?"
                        onConfirm={removeHandle.bind(null, id)}
                    >
                        <Button type="link">删除</Button>
                    </Popconfirm>
                    {+state !== 2 ? <Popconfirm
                        title="您确定要将此任务设置为完成吗?"
                        onConfirm={updateHandle.bind(null, id)}
                    >
                        <Button type="link">完成</Button>
                    </Popconfirm> : null}
                </>
            }
        },
    ];
    /* 初始组件的状态 */
    let [selectedIndex, setSelectedIndex] = useState(0),
        [tableData, setTableData] = useState([]),
        [tableLoading, setTableLoading] = useState(false),
        [modalVisible, setModalVisible] = useState(false),
        [confirmLoading, setConfirmLoading] = useState(false)
    let [formIns] = Form.useForm()
    // 第一次渲染完毕,判断redux中是否有全部的任务;如果没有,则进行异步的派发
    useEffect(() => {
        (async () => {
            if (!taskList) {
                setTableLoading(true)
                await queryAllList()
                setTableLoading(false)
            }
        })()
    }, [])
    // 依赖于redux中的全部任务 & 根据选中的状态信息,从全部任务中筛选出表格需要的数据
    useEffect(() => {
        if (!taskList) {
            setTableData([])
            return
        }
        if (selectedIndex !== 0) {
            taskList = taskList.filter(item => {
                return +item.state === +selectedIndex
            })
        }
        setTableData(taskList)
    }, [taskList, selectedIndex])
    // 关闭对话框
    const closeModal = () => {
        setModalVisible(false)
        setConfirmLoading(false)
        formIns.resetFields()
    }
    // 添加新任务
    const submit = async () => {
        try {
            await formIns.validateFields()
            let { task, time } = formIns.getFieldsValue()
            time = time.format('YYYY-MM-DD HH:mm:ss')
            setConfirmLoading(true)
            let { code } = await addTask(task, time)
            if (+code === 0) {
                closeModal()
                // query()
                queryAllList()// 派发任务,重新获取全部任务信息,同步到redux中
                message.success("恭喜添加成功")
            } else {
                message.error("很遗憾添加失败")
            }
        } catch (_) { }
        setConfirmLoading(false)
    }
    /* // 请求数据
    const query = async () => {
        setTableLoading(true)
        try {
            let { code, list } = await getTaskList(selectedIndex)
            if (+code !== 0) {
                list = []
            }
            setTableData(list)
        } catch (_) { }
        setTableLoading(false)
    } 
    useEffect(() => {
        query()
    }, [selectedIndex]) */
    // 删除任务
    const removeHandle = async (id) => {
        try {
            let { code } = await removeTask(id)
            if (+code === 0) {
                // query()
                deleteTaskById(id)// 派发任务,删除redux中的数据
                message.success("恭喜删除成功")
            } else {
                message.error("很遗憾删除失败")
            }
        } catch (_) { }
    }
    // 更新任务
    const updateHandle = async (id) => {
        try {
            let { code } = await completeTask(id)
            if (+code === 0) {
                // query()
                updateTaskById(id)// 派发任务,修改redux中的数据
                message.success("恭喜更新任务状态成功")
            } else {
                message.error("很遗憾更新失败")
            }
        } catch (_) { }
    }
    return <div className="task-box">
        {/* 头部 */}
        <div className="header">
            <h2 className="title">TASK OA 任务管理系统</h2>
            <Button type="primary" onClick={() => {
                setModalVisible(true)
            }}>新增任务</Button>
        </div>
        {/* 标签 */}
        <div className="tag-box">
            {['全部', '未完成', '已完成'].map((item, index) => {
                return <Tag key={index}
                    color={selectedIndex === index ? '#1677ff' : ''}
                    onClick={() => {
                        setSelectedIndex(index)
                    }}
                >{item}</Tag>
            })}
        </div>
        {/* 表格 */}
        <Table dataSource={tableData} columns={columns} loading={tableLoading} pagination={false} rowKey="id"></Table>
        {/* 对话框 & 表单 */}
        <Modal title="新增任务窗口" open={modalVisible} confirmLoading={confirmLoading} okText="提交信息" keyboard={false} maskClosable={false} onOk={submit} onCancel={closeModal}>
            <Divider />
            <Form
                layout="vertical"
                onFinish={() => { }}
                onFinishFailed={() => { }}
                form={formIns}
                initialValues={{ task: '', time: '' }}>
                <Form.Item
                    label="任务描述"
                    name="task"
                    validateTrigger="onBlur"
                    rules={[
                        {
                            required: true,
                            message: '任务描述是必填项'
                        },
                        {
                            min: 6,
                            message: '输入的内容至少六位'
                        }
                    ]}>
                    <Input.TextArea rows={4} />
                </Form.Item>

                <Form.Item
                    label="任务预期完成时间"
                    name="time"
                    validateTrigger="onBlur"
                    rules={[
                        {
                            required: true,
                            message: '预期完成时间是必填项'
                        },
                    ]}>
                    <DatePicker showTime />
                </Form.Item>
            </Form>
            <Divider />
        </Modal>
    </div >;
}

export default connect(
    state => state.task,
    action.task
)(Task);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值