Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Where to do calculations in redux?

General question, but will include a specific example: where is the correct place to loop through state/stored data to extract calculations?

So here, I need to make some calculations to display in a 'stats' sidebar that requires looping through each of an array of clients (could be a rather large number of clients) to pull out different props/values and add them all together. I did it in render just to get it to work which I know is incorrect, but does it happen still in the component and outside of render or in the reducer?

Of note, too, these will be updated values (a client can be marked as 'served' and then the stats sidebar will increment the number of served clients and decrement the number of to be served clients). But that is a little outside the scope of my general question.

Hows and whys are much appreciated and thanks a million!

    import React, { Component, PropTypes } from 'react';
    import { browserHistory } from 'react-router';
    import './ScheduleDayContainer.scss';
    import { connect } from 'react-redux';
    import { bindActionCreators } from 'redux';
    import * as ScheduleActions from '../../actions/ScheduleActions';

    class ScheduleDayContainer extends Component {
      static propTypes = {
        actions: PropTypes.object,
        clients: PropTypes.array.isRequired
      };

      constructor(props, context) {
        super(props, context);
      }

      componentWillMount() {
        // gets schedule (code removed because doesn't matter here) and clients array (used below)
        this.props.actions.fetchDaySchedule();
      }

      render() {
        const { clients } = this.props;

        const getStats = function (clients) {
          let totalClientsExpected = clients.length,
              totalHousehold = clients.length,
              totalServed = 0,
              totalNoShows = 0,
              totalUnverifiedExpected = 0,
              totalNotYetServed = 0;

          clients.forEach(function(client) {
            totalHousehold += client.family_count;
            client.served_at != null ? totalServed += 1 : totalNotYetServed += 1;
            // TODO: no show?
            client.verified_at === null ? totalUnverifiedExpected += 1 : null;
          });

          return {
            totalClientsExpected,
            totalHousehold,
            totalServed,
            totalNoShows,
            totalUnverifiedExpected,
            totalNotYetServed
          };
        };

        const stats = getStats(clients);

        return (
          <div className="day-container">
            <aside className="column">
              <div className="statistics-bar-container">
                <h3 className="statistics-title">Statistics</h3>
                <ul className="statistics-items">
                  <li className="statistics-item">
                    <p>Clients expected</p>
                    <span>{stats.totalClientsExpected}</span>
                  </li>
                  <li className="statistics-item">
                    <p>Total household members to be served</p>
                    <span>{stats.totalHousehold}</span>
                  </li>
                  <li className="statistics-item">
                    <p>Served</p>
                    <span>{stats.totalServed}</span>
                  </li>
                  <li className="statistics-item">
                    <p>Did not show</p>
                    <span>{stats.totalNoShows}</span>
                  </li>
                  <li className="statistics-item">
                    <p>Unverified clients expected</p>
                    <span>{stats.totalUnverifiedExpected}</span>
                  </li>
                  <li className="statistics-item">
                    <p>Yet to be served</p>
                    <span>{stats.totalNotYetServed}</span>
                  </li>
                </ul>
              </div>
            </aside>
          </div>
        );
      }
    }

    function mapStateToProps(state) {
      return {
        clients: state.schedule.clients
      };
    }

    function mapDispatchToProps(dispatch) {
      return {
        actions: bindActionCreators(ScheduleActions, dispatch)
      };
    }

    export default connect(
      mapStateToProps,
      mapDispatchToProps
    )(ScheduleDayContainer);

And then in the reducer:

    export default function scheduleReducer(state = initialState, action) {
      switch (action.type) {
        case types.FETCH_DAY:
          return {
            ...state,
            clients: action.data.clients,
            daySummary: action.data.summary,
            times: action.data.times,
            serviceDate: action.data.serviceDate,
            time: action.data.time
          };
        default:
          return state;
      }
    }
like image 726
megkadams Avatar asked Dec 23 '22 23:12

megkadams


1 Answers

It's generally agreed best practice to try and keep state as normalized (think relational database!) as possible.

Your derived data can be calculated on the fly by helper functions known as selectors. If some of these calculations are expensive you may want to consider the reselect library.

Some reading (sorry but they explain it far better than I can!):

http://redux.js.org/docs/recipes/ComputingDerivedData.html

http://www.thinkloop.com/article/extreme-decoupling-react-redux-selectors/

https://medium.com/@adamrackis/querying-a-redux-store-37db8c7f3b0f#.gl7g9suh2

like image 200
Mark Williams Avatar answered Jan 12 '23 13:01

Mark Williams