Throttle MapStatetoProps Calls in Connected Components

Viewed 146

I have a React application with a Redux store that connects to a websocket and displays the information to the user in a table. I have RxJs middleware that connects to the websocket and dispatches actions with the contents. Those then get picked up in a reducer and get saved to my state. I then mapStateToProps to display the data in my React components.

The issue is the updates come in faster than the renders can be. I could get 50 updates per second and if I render each time data comes in my app's performance would be terrible. I need a way to throttle the number of times mapStatetoProps gets called. I have a lot of components that map to this state and as of now I have 1 second throttles in shouldComponentUpdate for each one. It would be better if I could have the throttle of once per second at a lower level to avoid having duplicate logic across each one of my components. I also can't drop any of the 50 messages I get per second and just take the most recent because the data the user needs is spread across each message. Any ideas?

1 Answers

The issue is that the redux store is getting updated 50+ times a second and so the component gets rendered 50+ times a second. You only want the store to get updated once every n seconds but without any data loss. This is a common problem in high frequency data; Typically the solution is to use a cache.

So you need to cache the results and then every n seconds you push the cached messages into the messages array and then clear the cache.

It would look something like this.

your_websocket.on("new_message", message =>
  dispatch({ action: "ADD_TO_CACHE", message })
);

setInterval(() => {
    dispatch({ action: "PUSH_CACHE_TO_STATE" });
}, 1000);

const your_reducer = (state, action) => {
    /* -- snip -- */
    case "PUSH_CACHE_TO_STATE":
        newState.messages.push(...newState.messageCache);
        newState.messageCache = [];
        return newState;
    case "ADD_TO_CACHE":
        newState.messageCache.push(action.message);
        return newState;
}

This will push every new message into the cache, then every 1000 milliseconds the cache will get cleared and pushed into the messages array in your redux store.

Then in your component you just mapStateToProps like this:

connect(store => ({ messages: store.messages }))(YourComponent);

And your component will only be re-rendered every time the messages gets updated (once a second).

Related