Reputation: 454
I'm having this problem where my app updates too much. In my reducer I have an action of interest checkTasks
. It doesn't do much, checkTasks
goes through my list of current tasks, if any of them is expired, it removes them from the tasks
array to place them into the expiredTasks
array. My problem is that most of the time, checkTasks
doesn't do anything, only sometimes. But every time checkTasks
gets called my component gets rendered.
The mapStateToProps
of my component looks like this:
const mapStateToProps = (_state) => {
return {
tasks: _state.tasksState
}
}
and my component cares about 2 attributes from the state:
function Tasts({ tasksState: _tasksState}){
...
return <>
{renderExpired(_tasksState.expiredTasks)}
{renderTasks(_tasksState.tasks)}
</>
}
reducer
const reducer(_state = { tasks: [], expiredTasks: [] }, _action){
const newState = { ..._state };
...
case 'checkTasks':
for (let i = newState.tasks.length - 1; i >= 0; i--) {
if (isExpired(newState.tasks[i])) {
newState.expiredTasks.push(newState.tasks.splice(i, 1)[0]);
}
}
break;
...
return newState;
}
What I'm noticing is that every time (once per second) the checkTasks
action gets called, my component rerenders, even if practically the information in expiredTasks
and tasks
hasn't changed.
If I could change my mapStateToProps
to something like
const mapStateToProps = (_state) => {
return {
tasks: _state.tasksState.tasks,
expiredTasks: _state.tasksState.expiredTasks
}
}
That would probably stop the constant refreshes, however, that seems to break all refreshes. Including tasks moving from one array to the other. I don't know if there's a way to tell redux NOT to trigger an update from the reducer, although that's probably an anti-pattern.
I'm also trying not to go trough shouldComponentUpdate
given that evaluating whether I should update may require to check 2 object arrays against each other. Not triggering an update in the first place is much easier to handle than having to compare multiple arrays.
Upvotes: 0
Views: 58
Reputation: 15688
So if you're using redux-thunk, you can really enhance the logic inside your action-creators. In this case, we're going to check to see if any tasks have expired by actually pulling your reducer data into our action-creator. Then applying very similar logic to what you already are doing, we will decide whether or not we will actually dispatch a new action.
const checkTasks = () => {
return (dispatch, getState) => {
const currentState = getState().yourReducer //your reducer key goes here. Returns its current state
const expiredTasks = []
for(let i = currentState.tasks.length - 1; i >= 0; i--){
if(isExpired(currentState.tasks[i]){ //remember to import your isEmpty() logic
expiredTasks.push(currentState.tasks[i])
}
}
if(expiredTasks.length > 0){
dispatch({
type: "checkTasks"
})
}
}
}
If expiredTasks are empty then we won't send any action. No action, means no reducer update. Which means no component re-rendering.
Upvotes: 1