Maximus S
Maximus S

Reputation: 11115

Redux not re-rendering when nested object gets updated

Please refer to the below function for this question:

const mapStateToProps = (state) => {
  return {
    currentPlaylist: state.currentPlaylist,
    currentSong: state.currentSong,
    localPlaylists: state.playlistsBySource['local'],
    videoSize: state.videoSize,
  };
}

This is mapStateToProps function for a container. I expected the component that's connected to this container to be re-rendered every time state.playlistsBySource['local'] was updated. However, it didn't render even if state.playlistsBySource['local'] updated so I changed the code to below:

const mapStateToProps = (state) => {
  return {
    currentPlaylist: state.currentPlaylist,
    currentSong: state.currentSong,
    playlists: state.playlistsBySource,
    videoSize: state.videoSize,
  };
}

Then the connected component was correctly updated every time state.playlistsBySource['local'] was updated. But this is a kind of a waste because the connected component does not need the entire playlists.

Why is this exactly happening, and what's the correct way to solve it?

Update

I connect the component like this:

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

The render method of DumbComponent doesn't get called when state.playlistsBySource['local'] has been updated in the global store.

UPDATE2: Reducer seems to be immutable.. am I missing something?

case CONSTANTS.UPDATE_LOCAL_PLAYLIST:
  var playlistUpdated = transformedPlaylist(state[action.playlist.source], action);
  return updatedPlaylists(playlistUpdated, state);

function transformedPlaylist(state=[], action) {
  var oldPlaylist = _.find(state, (pl) =>
    pl.playlistName === action.playlist.playlistName);

  switch (action.type) {
    case CONSTANTS.RECEIVE_PLAYLIST:
    case CONSTANTS.UPDATE_LOCAL_PLAYLIST:
      return Object.assign({}, oldPlaylist, action.playlist, {
        isFetching: false,
      });
    default:
      return oldPlaylist
  }
}

function updatedPlaylists(playlistUpdated, oldPlaylists) {
  const source = playlistUpdated.source;
  const idxToUpdate = _.findIndex(oldPlaylists[source], (pl) =>
    pl.playlistName === playlistUpdated.playlistName
  );
  var playlists = Object.assign({}, oldPlaylists);
  playlists[source][idxToUpdate] = playlistUpdated;
  return playlists;
}

Upvotes: 3

Views: 2699

Answers (1)

markerikson
markerikson

Reputation: 67539

It would help if you could post the relevant code for your reducer. However, the vast majority of the time, the reason your component isn't updating is because you're directly mutating state in your reducer, instead of returning modified copies immutably. See the Redux FAQ answer at http://redux.js.org/docs/FAQ.html#react-not-rerendering for more information.

Update:

This line looks suspicious: playlists[source][idxToUpdate] = playlistUpdated;

Remember, if you are updating nested data immutably, you have to return copied and updated items all the way up to the top of your data.

It's a bit hard to tell exactly without seeing your whole state structure and actions in context, but I think it's pretty likely you are indeed doing direct mutation in one of your nested fields.

Upvotes: 7

Related Questions