React Redux Store updating, but component not re-rendering

Tonyhliu picture Tonyhliu · Jan 31, 2017 · Viewed 17.5k times · Source

Using the terminal to test my dispatched actions, Redux-logger shows that my state is being correctly updated. However, my component is not re-rendering as a result of the state change. I've looked at the SO answers regarding component not re-rendering, a majority of the responses claim that the state is being mutated; as a result, Redux won't re-render. However, I'm using Lodash's merge to deep-dup an object, I'm pretty sure I'm not returning a modified object. (Please see attached snippet below)

Would love to hear some advice from you guys, pulling my hair out on this one!

React Container Component

import { connect } from 'react-redux';
import { receiveUsers, receiveUser, refreshAll, requestUsers, requestUser } from '../../actions/user_actions';
import allUsers from '../../reducers/selectors';
import UserList from './user_list';

const mapStateToProps = (state) => ({
  users: allUsers(state), // allUsers (selector that takes the state specfically the user Object and returns an array of user Objects)
  state
});

const mapDispatchToProps = (dispatch) => ({
  requestUser: () => dispatch(requestUser()),
  requestUsers: () => dispatch(requestUsers()),
  receiveUsers: (users) => dispatch(receiveUsers(users)),
  receiveUser: (user) => dispatch(receiveUser(user)),
  refreshAll: (users) => dispatch(refreshAll(users))
});

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

React Presentational component

import React from 'react';

class UserList extends React.Component {
  render() {
    const { users, state } = this.props;

    const userItems = users.map((user, idx) => {
        return(<li key={idx}>{user.username}</li>);
    });

    return (
      <div>
        <ul>
          { userItems }
        </ul>
      </div>
    );
  }
}

export default UserList;

React Store

import { createStore, applyMiddleware } from 'redux';
import createLogger from 'redux-logger';
import RootReducer from '../reducers/root_reducer';

const logger = createLogger();
const configureStore = (preloadedState = {}) => {
  return createStore(
    RootReducer, 
    preloadedState,
    applyMiddleware(logger));
};

// const configureStore = createStore(rootReducer, applyMiddleware(logger));

// oddly enough, when I have the store as a constant and not a function that returns the store constant, dispatching actions through the terminal will correctly update the state and rerender the component 

export default configureStore;

React Selector

const allUsers = ({ users }) => {
  return Object.keys(users).map(id => (
    users[id]
  ));
};

export default allUsers;

Answer

Gus picture Gus · Mar 14, 2019

I had a similar problem, just in case someone stumbles upon this, I needed to clone the array in order to re-render the view:

export const addFieldRow = () => (
    (dispatch: any, getState: any) => {
        const state = getState();
        const myArrayOfObjects = myArrayOfObjectsProp(state);
        const newObject = {
            key: "",
            value: "",
        };
        myArrayOfObjects.push(newObject);
        dispatch(addFieldRowAction({ myArrayOfObjects: [...myArrayOfObjects] })); <== here
    }
);