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;
}
}
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With