React Redux - 如何使用选择器在 mapStateStateToProps 中派生并聚合数据?
React Redux - how to derive and then aggregate data in mapStateStateToProps using a selector?
我有一个组件 return 是一个名为 Progress
的对象,其中有一个名为 Results
的数组。该数组包含具有各种属性的对象,其中之一称为 total
{
Progress: {
count: 100,
results: [
{total: 4, ...},
{total: 10, ...},
...
]
}
}
组件 Dashboard
从状态获取数据并将其映射到进度 属性。
export class Dashboard extends Component {
static propTypes = {
progress: PropTypes.object.isRequired,
getProgress: PropTypes.func.isRequired,
totalResults: PropTypes.number.isRequired
}
componentDidMount() {
this.props.getProgress()
}
...
}
const selectProgress = state => state.progressReducer.progress
const mapStateToProps = state => ({
progress: selectProgress(state),
})
export default connect(mapStateToProps, { getProgress })(Dashboard)
我现在遇到的问题是如何添加一个新的 属性 派生自进度?
我知道我需要使用选择器,但我看不到 where/how 来执行此操作。
例如,我知道我可以像这样做一些琐碎(而且毫无意义)的事情:
const mapStateToProps = state => ({
progress: selectProgress(state),
count: selectProgress(state).count
})
它向组件添加了另一个 属性 count
(是的,它只是复制了 属性 内部进度,因此它毫无意义)。
我需要做的是这样的:
const mapStateToProps = state => ({
progress: selectProgress(state),
resultsTotal: <loop through the results array and sum the property total>
})
1 - 我试过的方法
我试过这个,尽管我知道它不应该是这样的。这是为了说明我正在尝试做的事情 - 在我取得进展之后,将它传递给某个函数来计算总数,然后 return 作为 属性 到组件:
const selectResults = progress => {
progress.results.reduce((acc, result) => {
acc + result.total
}, 0)
}
const mapStateToProps = state => ({
progress: selectProgress(state),
totalResults: selectResults(progress)
})
2 - 我试过的方法
我认为这会奏效,基本上让渲染视图在 JSX 中需要的点调用函数:
export class Dashboard extends Component {
static propTypes = {
progress: PropTypes.object.isRequired,
getProgress: PropTypes.func.isRequired,
}
componentDidMount() {
this.props.getProgress()
}
totalResults() {
if (this.props.progress.results)
return this.props.progress.results.reduce((acc, result) => {
acc + result.total
}, 0)
}
render() {
...
<SummaryCard title='Students' value={this.totalResults()} />
...
}
}
我现在想知道为什么这行不通 - 我不得不添加这一行:
if (this.props.progress.results)
因为执行此函数时进度当然是空的(即我猜是因为它在组件首次安装时执行,并且存储尚未 return 编辑数据)。
mapStateToProps
是一个函数。目前你正在使用一个简短的版本来立即 return 一个对象,但你可以将它作为一个复杂的函数,并且 return 最后一个对象:
const mapStateToProps = state => {
const progress = selectProgress(state);
return {
progress,
totalResults: progress !== undefined ? selectResults(progress) : undefined
}
}
我发现这个问题的一个解决方案是使用优秀的 reselect 库。来自他们的 github 页面:
- 选择器可以计算派生数据,允许 Redux 存储
最小的可能状态。
- 选择器是高效的。选择器不会重新计算,除非它的参数之一发生变化。
- 选择器是可组合的。它们可以用作其他选择器的输入。
所以我必须创建第二个选择器并将其链接到第一个选择器。
您可以在下面看到 progressSelector
会将其结果(progress
数据)传递到链中的下一个选择器 (totalResultsSelector
):
这是完整的组件:
import React, { Component } from 'react';
import Row from 'react-bootstrap/Row';
import Col from 'react-bootstrap/Col';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { createSelector } from 'reselect'
import { getProgress } from '../../actions/progress';
import SummaryCard from '../SummaryCard';
import { People } from 'react-bootstrap-icons';
import { Book } from 'react-bootstrap-icons';
import { Award } from 'react-bootstrap-icons';
import styles from './styles.scss';
export class Dashboard extends Component {
static propTypes = {
progress: PropTypes.object.isRequired,
getProgress: PropTypes.func.isRequired,
totalResults: PropTypes.number.isRequired
}
componentDidMount() {
this.props.getProgress()
}
render() {
return (
<div className={styles.wrapper}>
<Row xs={1} sm={3}>
<Col>
<SummaryCard title='Students' value={this.props.totalResults} icon={<People />} />
</Col>
<Col>
<SummaryCard title='Courses' value={this.props.progress.count} icon={<Book />} />
</Col>
<Col>
<SummaryCard title='Certified' value='0' icon={<Award />} />
</Col>
</Row>
</div>
)
}
}
const progressSelector = state => state.progressReducer.progress
const totalResultsSelector = createSelector (
progressSelector,
progress => {
if (!progress.results) return 0
const total = progress.results.reduce((acc, result) => {
return acc + result.total
}, 0)
return total
}
)
const mapStateToProps = state => ({
progress: progressSelector(state),
totalResults: totalResultsSelector(state)
})
export default connect(mapStateToProps, { getProgress })(Dashboard)
我有一个组件 return 是一个名为 Progress
的对象,其中有一个名为 Results
的数组。该数组包含具有各种属性的对象,其中之一称为 total
{
Progress: {
count: 100,
results: [
{total: 4, ...},
{total: 10, ...},
...
]
}
}
组件 Dashboard
从状态获取数据并将其映射到进度 属性。
export class Dashboard extends Component {
static propTypes = {
progress: PropTypes.object.isRequired,
getProgress: PropTypes.func.isRequired,
totalResults: PropTypes.number.isRequired
}
componentDidMount() {
this.props.getProgress()
}
...
}
const selectProgress = state => state.progressReducer.progress
const mapStateToProps = state => ({
progress: selectProgress(state),
})
export default connect(mapStateToProps, { getProgress })(Dashboard)
我现在遇到的问题是如何添加一个新的 属性 派生自进度?
我知道我需要使用选择器,但我看不到 where/how 来执行此操作。
例如,我知道我可以像这样做一些琐碎(而且毫无意义)的事情:
const mapStateToProps = state => ({
progress: selectProgress(state),
count: selectProgress(state).count
})
它向组件添加了另一个 属性 count
(是的,它只是复制了 属性 内部进度,因此它毫无意义)。
我需要做的是这样的:
const mapStateToProps = state => ({
progress: selectProgress(state),
resultsTotal: <loop through the results array and sum the property total>
})
1 - 我试过的方法
我试过这个,尽管我知道它不应该是这样的。这是为了说明我正在尝试做的事情 - 在我取得进展之后,将它传递给某个函数来计算总数,然后 return 作为 属性 到组件:
const selectResults = progress => {
progress.results.reduce((acc, result) => {
acc + result.total
}, 0)
}
const mapStateToProps = state => ({
progress: selectProgress(state),
totalResults: selectResults(progress)
})
2 - 我试过的方法
我认为这会奏效,基本上让渲染视图在 JSX 中需要的点调用函数:
export class Dashboard extends Component {
static propTypes = {
progress: PropTypes.object.isRequired,
getProgress: PropTypes.func.isRequired,
}
componentDidMount() {
this.props.getProgress()
}
totalResults() {
if (this.props.progress.results)
return this.props.progress.results.reduce((acc, result) => {
acc + result.total
}, 0)
}
render() {
...
<SummaryCard title='Students' value={this.totalResults()} />
...
}
}
我现在想知道为什么这行不通 - 我不得不添加这一行:
if (this.props.progress.results)
因为执行此函数时进度当然是空的(即我猜是因为它在组件首次安装时执行,并且存储尚未 return 编辑数据)。
mapStateToProps
是一个函数。目前你正在使用一个简短的版本来立即 return 一个对象,但你可以将它作为一个复杂的函数,并且 return 最后一个对象:
const mapStateToProps = state => {
const progress = selectProgress(state);
return {
progress,
totalResults: progress !== undefined ? selectResults(progress) : undefined
}
}
我发现这个问题的一个解决方案是使用优秀的 reselect 库。来自他们的 github 页面:
- 选择器可以计算派生数据,允许 Redux 存储 最小的可能状态。
- 选择器是高效的。选择器不会重新计算,除非它的参数之一发生变化。
- 选择器是可组合的。它们可以用作其他选择器的输入。
所以我必须创建第二个选择器并将其链接到第一个选择器。
您可以在下面看到 progressSelector
会将其结果(progress
数据)传递到链中的下一个选择器 (totalResultsSelector
):
这是完整的组件:
import React, { Component } from 'react';
import Row from 'react-bootstrap/Row';
import Col from 'react-bootstrap/Col';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { createSelector } from 'reselect'
import { getProgress } from '../../actions/progress';
import SummaryCard from '../SummaryCard';
import { People } from 'react-bootstrap-icons';
import { Book } from 'react-bootstrap-icons';
import { Award } from 'react-bootstrap-icons';
import styles from './styles.scss';
export class Dashboard extends Component {
static propTypes = {
progress: PropTypes.object.isRequired,
getProgress: PropTypes.func.isRequired,
totalResults: PropTypes.number.isRequired
}
componentDidMount() {
this.props.getProgress()
}
render() {
return (
<div className={styles.wrapper}>
<Row xs={1} sm={3}>
<Col>
<SummaryCard title='Students' value={this.props.totalResults} icon={<People />} />
</Col>
<Col>
<SummaryCard title='Courses' value={this.props.progress.count} icon={<Book />} />
</Col>
<Col>
<SummaryCard title='Certified' value='0' icon={<Award />} />
</Col>
</Row>
</div>
)
}
}
const progressSelector = state => state.progressReducer.progress
const totalResultsSelector = createSelector (
progressSelector,
progress => {
if (!progress.results) return 0
const total = progress.results.reduce((acc, result) => {
return acc + result.total
}, 0)
return total
}
)
const mapStateToProps = state => ({
progress: progressSelector(state),
totalResults: totalResultsSelector(state)
})
export default connect(mapStateToProps, { getProgress })(Dashboard)