41、Redux数据存储API的使用指南

Redux数据存储API的使用指南

1. 数据转换与ID追踪的重要性

在选择器中转换数据可确保在需要相同数据视图时保持一致性,但这意味着连接的组件不再处理数据存储中的原始数据。因此,依赖一个组件接收的数据来驱动另一个组件的行为可能会导致问题。为了解决这个问题,我们使用ID值来跟踪用户选择编辑的对象。

2. Redux数据存储API概述

在大多数React应用中,通常通过React - Redux包来访问Redux数据存储,该包将数据存储功能映射到组件的props。不过,Redux也提供了完整的API,可直接访问数据存储功能。下面是对Redux数据存储API的详细介绍:
| 问题 | 解决方案 |
| — | — |
| 访问Redux数据存储API | 使用 createStore 方法返回的数据存储对象定义的方法 |
| 观察数据存储变化 | 使用 subscribe 方法 |
| 调度操作 | 使用 dispatch 方法 |
| 创建自定义连接器 | 将组件的props映射到数据存储功能 |
| 为数据存储添加功能 | 创建reducer增强器 |
| 在操作传递给reducer之前处理操作 | 创建中间件函数 |
| 扩展数据存储API | 创建增强器函数 |
| 将组件的props合并到数据存储映射中 | 使用 connect 函数的可选参数 |

3. 准备工作

继续使用之前创建并修改过的项目,无需进行更改。打开新的命令提示符,导航到项目文件夹,运行以下命令启动开发工具:

npm start

启动开发工具后,会打开一个新的浏览器窗口并显示应用内容。

4. 使用Redux数据存储API

createStore 函数返回的对象可通过以下四个方法直接使用:
| 方法名 | 描述 |
| — | — |
| getState() | 返回数据存储中的数据 |
| subscribe(listener) | 注册一个函数,每次数据存储发生更改时调用该函数 |
| dispatch(action) | 接受一个操作(通常由操作创建者生成),并将其发送到数据存储以由reducer处理 |
| replaceReducer(next) | 替换数据存储用于处理操作的reducer,此方法在大多数项目中用处不大,中间件是更改数据存储行为更有用的机制 |

5. 获取数据存储状态

getState 方法可返回数据存储中的数据,以下是一个示例:

// src/store/StoreAccess.js
import React, { Component } from "react";
export class StoreAccess extends Component {
    render() {
        return <div className="bg-info">
            <pre className="text-white">
                { JSON.stringify(this.props.store.getState(), null, 2) }
            </pre>
        </div>
    }
}

App.js 中添加网格布局以显示新组件:

// src/App.js
import React, { Component } from "react";
import { Provider } from "react-redux";
import dataStore from "./store";
import { Selector } from "./Selector";
import { ProductDisplay } from "./ProductDisplay";
import { SupplierDisplay } from "./SupplierDisplay";
import { StoreAccess } from "./store/StoreAccess";
export default class App extends Component {
    render() {
        return <div className="container-fluid">
            <div className="row">
                <div className="col-3">
                    <StoreAccess store={ dataStore } />
                </div>
                <div className="col">
                    <Provider store={ dataStore }>
                        <Selector>
                            <ProductDisplay name="Products" />
                            <SupplierDisplay name="Suppliers" />
                        </Selector>
                    </Provider>
                </div>
            </div>
        </div>
    }
}

由于存储中可能有大量数据,我们将显示JSON文本放在单独的列中。 getState 方法返回的数据包含所有内容,包括 modelData stateData 属性。

6. 聚焦特定数据

为了更方便地跟踪数据存储的内容,我们可以聚焦 getState 方法返回的部分数据。以下是修改后的 StoreAccess 组件:

// src/store/StoreAccess.js
import React, { Component } from "react";
export class StoreAccess extends Component {
    constructor(props) {
        super(props);
        this.selectors = {
            product: (storeState) => storeState.modelData.products[0],
            state: (storeState) => storeState.stateData
        }
    }
    render() {
        return <div className="bg-info">
            <pre className="text-white">
                { JSON.stringify(this.selectData(), null, 2) }
            </pre>
        </div>
    }
    selectData() {
        let storeState = this.props.store.getState();
        return Object.entries(this.selectors).map(([k, v]) => [k, v(storeState)])
           .reduce((result, [k, v]) => ({ ...result, [k]: v}), {});
    }
}

通过定义 selectors 对象和 selectData 方法,我们可以选择更易于管理的数据部分进行显示。

7. 观察数据存储变化

getState 方法返回的是数据存储的快照,当存储发生变化时,它不会自动更新。Redux提供了 subscribe 方法来接收数据存储更改的通知。以下是使用 subscribe 方法的示例:

// src/store/StoreAccess.js
import React, { Component } from "react";
export class StoreAccess extends Component {
    constructor(props) {
        super(props);
        this.selectors = {
            product: (storeState) => storeState.modelData.products[0],
            state: (storeState) => storeState.stateData
        }
        this.state = this.selectData();
    }
    render() {
        return <div className="bg-info">
            <pre className="text-white">
                { JSON.stringify(this.state, null, 2) }
            </pre>
        </div>
    }
    selectData() {
        let storeState = this.props.store.getState();
        return Object.entries(this.selectors).map(([k, v]) => [k, v(storeState)])
           .reduce((result, [k, v]) => ({ ...result, [k]: v}), {});
    }
    handleDataStoreChange() {
        let newData = this.selectData();
        Object.keys(this.selectors)
           .filter(key => this.state[key] !== newData[key])
           .forEach(key => this.setState({ [key]: newData[key]}));
    }
    componentDidMount() {
        this.unsubscriber =
            this.props.store.subscribe(() => this.handleDataStoreChange());
    }
    componentWillUnmount() {
        this.unsubscriber();
    }
}

componentDidMount 方法中订阅更新,在 componentWillUnmount 方法中取消订阅。当数据存储发生变化时, handleDataStoreChange 方法会检查组件渲染的数据是否发生变化,并使用 setState 方法触发更新。

8. 调度操作

可以使用 dispatch 方法调度操作,这与之前使用React - Redux包时调度多个操作的 dispatch 相同。以下是添加按钮调度操作的示例:

// src/store/StoreAccess.js
import React, { Component } from "react";
import { startCreatingProduct } from "./stateActions";
export class StoreAccess extends Component {
    constructor(props) {
        super(props);
        this.selectors = {
            product: (storeState) => storeState.modelData.products[0],
            state: (storeState) => storeState.stateData
        }
        this.state = this.selectData();
    }
    render() {
        return <React.Fragment>
            <div className="text-center">
                <button className="btn btn-primary m-1"
                    onClick={ this.dispatchAction }>
                        Dispatch Action
                </button>
            </div>
            <div className="bg-info">
                <pre className="text-white">
                    { JSON.stringify(this.state, null, 2) }
                </pre>
            </div>
        </React.Fragment>
    }
    dispatchAction = () => {
        this.props.store.dispatch(startCreatingProduct())
    }
    selectData() {
        let storeState = this.props.store.getState();
        return Object.entries(this.selectors).map(([k, v]) => [k, v(storeState)])
           .reduce((result, [k, v]) => ({ ...result, [k]: v}), {});
    }
    handleDataStoreChange() {
        let newData = this.selectData();
        Object.keys(this.selectors)
           .filter(key => this.state[key] !== newData[key])
           .forEach(key => this.setState({ [key]: newData[key]}));
    }
    componentDidMount() {
        this.unsubscriber =
            this.props.store.subscribe(() => this.handleDataStoreChange());
    }
    componentWillUnmount() {
        this.unsubscriber();
    }
}

点击按钮会调用 dispatchAction 方法,该方法调用 startCreatingProduct 操作创建者并将结果传递给数据存储的 dispatch 方法。

9. 创建自定义连接器组件

利用获取当前数据、接收更改通知和调度操作的能力,我们可以创建一个基本的连接器组件,它类似于示例应用中使用的React - Redux包。以下是自定义连接器组件的代码:

// src/store/CustomConnector.js
import React, { Component } from "react";
export const CustomConnectorContext = React.createContext();
export class CustomConnectorProvider extends Component {
    render() {
        return <CustomConnectorContext.Provider value={ this.props.dataStore }>
            { this.props.children }
        </CustomConnectorContext.Provider>
    }
}
export class CustomConnector extends React.Component {
    static contextType = CustomConnectorContext;
    constructor(props, context) {
        super(props, context);
        this.state = this.selectData();
        this.functionProps = Object.entries(this.props.dispatchers)
           .map(([k, v]) => [k, (...args) => this.context.dispatch(v(...args))])
           .reduce((result, [k, v]) => ({...result, [k]: v}), {});
    }
    render() {
        return  React.Children.map(this.props.children, c =>
            React.cloneElement(c, { ...this.state, ...this.functionProps }))
    }
    selectData() {
        let storeState = this.context.getState();
        return Object.entries(this.props.selectors).map(([k, v]) =>
                [k, v(storeState)])
           .reduce((result, [k, v]) => ({ ...result, [k]: v}), {});
    }
    handleDataStoreChange() {
        let newData = this.selectData();
        Object.keys(this.props.selectors)
           .filter(key => this.state[key] !== newData[key])
           .forEach(key => this.setState({ [key]: newData[key]}));
    }
    componentDidMount() {
        this.unsubscriber =
            this.context.subscribe(() => this.handleDataStoreChange());
    }
    componentWillUnmount() {
        this.unsubscriber();
    }
}

使用 CustomConnectorProvider 组件通过上下文API使数据存储可用, CustomConnector 组件接收选择器和操作创建者props。以下是在 App 组件中使用自定义连接器的示例:

// src/App.js
import React, { Component } from "react";
import { Provider } from "react-redux";
import dataStore, { deleteProduct } from "./store";
import { Selector } from "./Selector";
import { ProductDisplay } from "./ProductDisplay";
import { SupplierDisplay } from "./SupplierDisplay";
import { StoreAccess } from "./store/StoreAccess";
import { CustomConnector, CustomConnectorProvider } from "./store/CustomConnector";
import { startEditingProduct } from "./store/stateActions";
import { ProductTable } from "./ProductTable";
const selectors = {
    products: (store) => store.modelData.products
}
const dispatchers = {
    editCallback: startEditingProduct,
    deleteCallback: deleteProduct
}
export default class App extends Component {
    render() {
        return <div className="container-fluid">
            <div className="row">
                <div className="col-3">
                    <StoreAccess store={ dataStore } />
                </div>
                <div className="col">
                    <Provider store={ dataStore }>
                        <Selector>
                            <ProductDisplay name="Products" />
                            <SupplierDisplay name="Suppliers" />
                        </Selector>
                    </Provider>
                </div>
            </div>
            <div className="row">
                <div className="col">
                    <CustomConnectorProvider dataStore={ dataStore }>
                        <CustomConnector selectors={ selectors }
                                dispatchers={ dispatchers }>
                            <ProductTable/>
                        </CustomConnector>
                    </CustomConnectorProvider>
                </div>
            </div>
        </div>
    }
}

通过这种方式,应用会显示第二个产品表,两个表都会反映数据的变化。

综上所述,Redux数据存储API提供了强大的功能,通过合理使用这些API,我们可以更灵活地管理和操作数据存储。无论是观察数据变化、调度操作还是创建自定义连接器,都能帮助我们构建更高效、更稳定的React应用。在实际开发中,需要根据具体需求选择合适的方法和技术,以达到最佳的开发效果。

Redux数据存储API的使用指南

10. 自定义连接器的注意事项

虽然我们创建了自定义连接器,但在实际项目中并不推荐使用。React - Redux包具有更多的特性,并且经过了充分的测试。不过,将核心React特性与Redux数据存储API相结合,为我们展示了如何创建高级功能。以下是自定义连接器的使用流程:

graph LR
    A[创建CustomConnectorProvider组件] --> B[传递数据存储对象]
    B --> C[创建CustomConnector组件]
    C --> D[接收选择器和操作创建者props]
    D --> E[处理选择器和操作创建者]
    E --> F[将处理结果映射到子组件props]
11. 总结与应用建议

Redux数据存储API为我们提供了直接访问数据存储的能力,包括获取数据、观察变化、调度操作等。以下是对各功能的总结:
| 功能 | 方法 | 说明 |
| — | — | — |
| 获取数据存储状态 | getState() | 返回数据存储中的数据 |
| 观察数据存储变化 | subscribe(listener) | 注册一个函数,每次数据存储发生更改时调用该函数 |
| 调度操作 | dispatch(action) | 接受一个操作(通常由操作创建者生成),并将其发送到数据存储以由reducer处理 |
| 创建自定义连接器 | 自定义组件 | 将组件的props映射到数据存储功能 |

在实际应用中,我们可以根据具体需求选择合适的方法。如果只是简单地使用数据存储功能,React - Redux包提供的映射机制已经足够;如果需要更高级的功能,如自定义连接器或对数据存储进行更精细的控制,可以使用Redux数据存储API。

同时,在使用 subscribe 方法时,需要注意避免不必要的更新。可以通过比较前后数据来判断是否需要更新组件状态,以提高应用的性能。以下是一个性能优化的步骤列表:
1. 在 handleDataStoreChange 方法中,比较新旧数据。
2. 只对发生变化的数据进行更新。
3. 使用 shouldComponentUpdate 生命周期方法进一步优化。

12. 代码示例回顾

以下是我们在前面介绍的主要代码示例的回顾:
- 获取数据存储状态

// src/store/StoreAccess.js
import React, { Component } from "react";
export class StoreAccess extends Component {
    render() {
        return <div className="bg-info">
            <pre className="text-white">
                { JSON.stringify(this.props.store.getState(), null, 2) }
            </pre>
        </div>
    }
}
  • 观察数据存储变化
// src/store/StoreAccess.js
import React, { Component } from "react";
export class StoreAccess extends Component {
    constructor(props) {
        super(props);
        this.selectors = {
            product: (storeState) => storeState.modelData.products[0],
            state: (storeState) => storeState.stateData
        }
        this.state = this.selectData();
    }
    render() {
        return <div className="bg-info">
            <pre className="text-white">
                { JSON.stringify(this.state, null, 2) }
            </pre>
        </div>
    }
    selectData() {
        let storeState = this.props.store.getState();
        return Object.entries(this.selectors).map(([k, v]) => [k, v(storeState)])
           .reduce((result, [k, v]) => ({ ...result, [k]: v}), {});
    }
    handleDataStoreChange() {
        let newData = this.selectData();
        Object.keys(this.selectors)
           .filter(key => this.state[key] !== newData[key])
           .forEach(key => this.setState({ [key]: newData[key]}));
    }
    componentDidMount() {
        this.unsubscriber =
            this.props.store.subscribe(() => this.handleDataStoreChange());
    }
    componentWillUnmount() {
        this.unsubscriber();
    }
}
  • 调度操作
// src/store/StoreAccess.js
import React, { Component } from "react";
import { startCreatingProduct } from "./stateActions";
export class StoreAccess extends Component {
    constructor(props) {
        super(props);
        this.selectors = {
            product: (storeState) => storeState.modelData.products[0],
            state: (storeState) => storeState.stateData
        }
        this.state = this.selectData();
    }
    render() {
        return <React.Fragment>
            <div className="text-center">
                <button className="btn btn-primary m-1"
                    onClick={ this.dispatchAction }>
                        Dispatch Action
                </button>
            </div>
            <div className="bg-info">
                <pre className="text-white">
                    { JSON.stringify(this.state, null, 2) }
                </pre>
            </div>
        </React.Fragment>
    }
    dispatchAction = () => {
        this.props.store.dispatch(startCreatingProduct())
    }
    selectData() {
        let storeState = this.props.store.getState();
        return Object.entries(this.selectors).map(([k, v]) => [k, v(storeState)])
           .reduce((result, [k, v]) => ({ ...result, [k]: v}), {});
    }
    handleDataStoreChange() {
        let newData = this.selectData();
        Object.keys(this.selectors)
           .filter(key => this.state[key] !== newData[key])
           .forEach(key => this.setState({ [key]: newData[key]}));
    }
    componentDidMount() {
        this.unsubscriber =
            this.props.store.subscribe(() => this.handleDataStoreChange());
    }
    componentWillUnmount() {
        this.unsubscriber();
    }
}
  • 创建自定义连接器
// src/store/CustomConnector.js
import React, { Component } from "react";
export const CustomConnectorContext = React.createContext();
export class CustomConnectorProvider extends Component {
    render() {
        return <CustomConnectorContext.Provider value={ this.props.dataStore }>
            { this.props.children }
        </CustomConnectorContext.Provider>
    }
}
export class CustomConnector extends React.Component {
    static contextType = CustomConnectorContext;
    constructor(props, context) {
        super(props, context);
        this.state = this.selectData();
        this.functionProps = Object.entries(this.props.dispatchers)
           .map(([k, v]) => [k, (...args) => this.context.dispatch(v(...args))])
           .reduce((result, [k, v]) => ({...result, [k]: v}), {});
    }
    render() {
        return  React.Children.map(this.props.children, c =>
            React.cloneElement(c, { ...this.state, ...this.functionProps }))
    }
    selectData() {
        let storeState = this.context.getState();
        return Object.entries(this.props.selectors).map(([k, v]) =>
                [k, v(storeState)])
           .reduce((result, [k, v]) => ({ ...result, [k]: v}), {});
    }
    handleDataStoreChange() {
        let newData = this.selectData();
        Object.keys(this.props.selectors)
           .filter(key => this.state[key] !== newData[key])
           .forEach(key => this.setState({ [key]: newData[key]}));
    }
    componentDidMount() {
        this.unsubscriber =
            this.context.subscribe(() => this.handleDataStoreChange());
    }
    componentWillUnmount() {
        this.unsubscriber();
    }
}

通过对这些代码的理解和应用,我们可以更好地掌握Redux数据存储API的使用,从而构建出更强大、更灵活的React应用。在开发过程中,不断实践和总结经验,能够让我们更加熟练地运用这些技术,提高开发效率和应用质量。

内容概要:本文围绕EKF SLAM(扩展卡尔曼滤波同步定位与地图构建)的性能展开多项对比实验研究,重点分析在稀疏与稠密landmark环境下、预测与更新步骤同时进行与非同时进行的情况下的系统性能差异,并进一步探讨EKF SLAM在有色噪声干扰下的鲁棒性表现。实验考虑了不确定性因素的影响,旨在评估不同条件下算法的定位精度与地图构建质量,为实际应用中EKF SLAM的优化提供依据。文档还提及多智能体系统在遭受DoS攻击下的弹性控制研究,但核心内容聚焦于SLAM算法的性能测试与分析。; 适合人群:具备一定机器人学、状态估计或自动驾驶基础知识的科研人员及工程技术人员,尤其是从事SLAM算法研究或应用开发的硕士、博士研究生和相关领域研发人员。; 使用场景及目标:①用于比较EKF SLAM在不同landmark密度下的性能表现;②分析预测与更新机制同步与否对滤波器稳定性与精度的影响;③评估系统在有色噪声等非理想观测条件下的适应能力,提升实际部署中的可靠性。; 阅读建议:建议结合MATLAB仿真代码进行实验复现,重点关注状态协方差传播、观测更新频率与噪声模型设置等关键环节,深入理解EKF SLAM在复杂环境下的行为特性。稀疏 landmark 与稠密 landmark 下 EKF SLAM 性能对比实验,预测更新同时进行与非同时进行对比 EKF SLAM 性能对比实验,EKF SLAM 在有色噪声下性能实验
内容概要:本文围绕“基于主从博弈的售电商多元零售套餐设计与多级市场购电策略”展开,结合Matlab代码实现,提出了一种适用于电力市场化环境下的售电商优化决策模型。该模型采用主从博弈(Stackelberg Game)理论构建售电商与用户之间的互动关系,售电商作为领导者制定电价套餐策略,用户作为跟随者响应电价并调整用电行为。同时,模型综合考虑售电商在多级电力市场(如日前市场、实时市场)中的【顶级EI复现】基于主从博弈的售电商多元零售套餐设计与多级市场购电策略(Matlab代码实现)购电组合优化,兼顾成本最小化与收益最大化,并引入不确定性因素(如负荷波动、可再生能源出力变化)进行鲁棒或随机优化处理。文中提供了完整的Matlab仿真代码,涵盖博弈建模、优化求解(可能结合YALMIP+CPLEX/Gurobi等工具)、结果可视化等环节,具有较强的可复现性和工程应用价值。; 适合人群:具备一定电力系统基础知识、博弈论初步认知和Matlab编程能力的研究生、科研人员及电力市场从业人员,尤其适合从事电力市场运营、需求响应、售电策略研究的相关人员。; 使用场景及目标:① 掌握主从博弈在电力市场中的建模方法;② 学习售电商如何设计差异化零售套餐以引导用户用电行为;③ 实现多级市场购电成本与风险的协同优化;④ 借助Matlab代码快速复现顶级EI期刊论文成果,支撑科研项目或实际系统开发。; 阅读建议:建议读者结合提供的网盘资源下载完整代码与案例数据,按照文档目录顺序逐步学习,重点关注博弈模型的数学表达与Matlab实现逻辑,同时尝试对目标函数或约束条件进行扩展改进,以深化理解并提升科研创新能力。
内容概要:本文介绍了基于粒子群优化算法(PSO)的p-Hub选址优化问基于粒子群优化算法的p-Hub选址优化(Matlab代码实现)题的Matlab代码实现,旨在解决物流与交通网络中枢纽节点的最优选址问题。通过构建数学模型,结合粒子群算法的全局寻优能力,优化枢纽位置及分配策略,提升网络传输效率并降低运营成本。文中详细阐述了算法的设计思路、实现步骤以及关键参数设置,并提供了完整的Matlab仿真代码,便于读者复现和进一步改进。该方法适用于复杂的组合优化问题,尤其在大规模网络选址中展现出良好的收敛性和实用性。; 适合人群:具备一定Matlab编程基础,从事物流优化、智能算法研究或交通运输系统设计的研究生、科研人员及工程技术人员;熟悉优化算法基本原理并对实际应用场景感兴趣的从业者。; 使用场景及目标:①应用于物流中心、航空枢纽、快递分拣中心等p-Hub选址问题;②帮助理解粒子群算法在离散优化问题中的编码与迭代机制;③为复杂网络优化提供可扩展的算法框架,支持进一步融合约束条件或改进算法性能。; 阅读建议:建议读者结合文中提供的Matlab代码逐段调试运行,理解算法流程与模型构建逻辑,重点关注粒子编码方式、适应度函数设计及约束处理策略。可尝试替换数据集或引入其他智能算法进行对比实验,以深化对优化效果和算法差异的理解。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值