Реакция Redux - как получить, а затем агрегировать данные в mapStateStateToProps с помощью селектора? - PullRequest
0 голосов
/ 19 апреля 2020

У меня есть компонент, который возвращает объект с именем Progress, внутри которого находится массив с именем Results. В этом массиве есть объекты с различными свойствами, одно из которых называется total

{ 
  Progress: {
    count: 100,
    results: [
     {total: 4, ...}, 
     {total: 10, ...},
     ...
    ]
  }
}

Компонент Dashboard получает данные из состояния и сопоставляет их со свойством Progress.

 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)

Проблема, с которой я столкнулся сейчас, заключается в том, как добавить новое свойство, полученное из хода выполнения?

Я понимаю, что должен использовать селектор, но не могу понять, где и как это сделать.

Например, я знаю, что могу сделать что-то тривиальное (и бессмысленное), например:

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 - что я пробовал

I попробовал это, хотя я понимаю, что это не так. Надеюсь, это иллюстрирует то, что я пытаюсь сделать - ПОСЛЕ того, как у меня есть прогресс, передайте его какой-то функции для вычисления суммы и верните ее как свойство компоненту:

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)

, поскольку при выполнении этой функции прогресс, конечно, пустой (ie Я полагаю, потому что он выполняется при первом монтировании компонента, и магазин еще не вернул данные).

Ответы [ 2 ]

0 голосов
/ 19 апреля 2020

Одним из решений, которое я нашел для этой проблемы, является использование превосходной библиотеки 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)
0 голосов
/ 19 апреля 2020

mapStateToProps - это функция. В настоящее время вы используете короткую версию для немедленного возврата объекта, но вы можете иметь ее как сложную функцию и в конце вернуть объект:

const mapStateToProps = state => {
    const progress = selectProgress(state);
    return {
        progress,
        totalResults: progress !== undefined ? selectResults(progress) : undefined
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...