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应用。在开发过程中,不断实践和总结经验,能够让我们更加熟练地运用这些技术,提高开发效率和应用质量。
超级会员免费看
26

被折叠的 条评论
为什么被折叠?



