I have a React-Redux app which displays a dashboard page when the user first logs on. The dashboard displays a number of statistics/graphs etc. which are components most (not all) of which are filtered based on a state site value. This site value is initially set when the user logs on and is derived from their profile.
The components mostly follow the pattern of using the componentDidMount lifecycle event to call a thunk method. Within the thunk method the site value is retrieved from redux state and passed in the database query. Reducer then adds results to state. Standard stuff. The redux state is fairly flat i.e. the state for the statistics/graphs etc. are not nested under the selected site but are their own objects off the root.
On the dashboard is also a dropdownlist which contains all sites. Initially this is set to the users default site. This dropdown is intended to allow the user to see statistics/graphs for sites other than their default.
What I would like is for all the (site specific) dashboard components to refresh when the user selects a different site from the dropdownlist. The problem I am having is how do I get these components to refresh? or more specifically, how to get their state to refresh.
UPDATE: I tried two different approaches.
- In the thunk action for handling the site change effect (changeSite) I added dispatch calls to each components thunk action. Although this worked I didn't like the fact that the changeSite thunk needed to know about the other components and what action creators to call. i.e. thunk action looked like:
changeSite: (siteId: number): AppThunkAction<any> => async (dispatch) => {
const promiseSiteUpdate = dispatch({ type: 'CHANGE_SITE', siteId });
await Promise.all([promiseSiteUpdate]);
dispatch(AncillariesStore.actionCreators.requestExpiring(siteId));
dispatch(AncillariesStore.actionCreators.requestExpired(siteId));
dispatch(FaultsStore.actionCreators.requestFaults(siteId));
dispatch(AssetsStore.actionCreators.requestAssetsCount(siteId));
dispatch(LicencesStore.actionCreators.requestLicencesCount(siteId));
dispatch(MaintenancesStore.actionCreators.requestMaintenancesCount(siteId));
dispatch(FaultsStore.actionCreators.requestFaultsCount(siteId));
}
- Within a dependant component, included the Site value in the component properties, hooked into the componentDidUpdate lifecycle event. Checked if the Site had changed and then called the thunk action for updating the component state. I preferred this approach as it kept the business logic within the component so the changeSite thunk could now become a simple reducer call. An example of a dependent (site faults) component looks like this:
type FaultsProps =
{
faults: FaultsStore.FaultsItem[],
currentSiteId: number
}
& typeof FaultsStore.actionCreators;
const mapStateToProps = (state: ApplicationState) => {
return {
faults: state.faults?.faults,
currentSiteId: state.sites?.currentSiteId
}
}
class FaultyListContainer extends React.PureComponent<FaultsProps> {
public componentDidMount() {
// First load.
this.props.requestFaults(this.props.currentSiteId);
}
public componentDidUpdate(prevProps: FaultsProps) {
// Update state if site has changed.
if(prevProps.currentSiteId !== this.props.currentSiteId) {
this.props.requestFaults(this.props.currentSiteId);
}
}
public render() {
return React.createElement(FaultsList, {faults: this.props.faults});
}
}
export default connect(
mapStateToProps,
FaultsStore.actionCreators
)(FaultyListContainer as any);
Is #2 the best approach? is there a better way?