Search code examples
javascriptreduxsaga

How to cause a rerender only if one object changes in a useSelector


Got an issue where I have an object with multiple values in my state but I only need to listen to one for the change.

Example:

On first re-render I might see something like this.

loading: {
  objectone: false,
  objecttwo: false, 
  objectthree: false, 
}

On my second re-render I might see the state change to something like this:}

    loading: {
      objectone: true,
      objecttwo: false, 
      objectthree: false, 
    }

The state then changes further to update the other two objects to true.

    loading: {
      objectone: true,
      objecttwo: true, 
      objectthree: true, 
    }

Each time one of the objects in the loading state changes it causes a re-render in my useSelector.

If I wanted just the information in the first object objectone. How could I write this in my useSelector so that it doesn't trigger a re-render when the other two objects update?

Things I've tried:

before

  const loadingStatus = useSelector(({ loading }) => loading);

after

  const loadingStatus = useSelector(({ loading }) => {
    return loading.objectone ? true : false;
  });

Solution

  • Here is an example, your second selector should work and not re render when objectone is not changed:

    const { Provider, useDispatch, useSelector } = ReactRedux;
    const { createStore, applyMiddleware, compose } = Redux;
    const { createSelector } = Reselect;
    
    const initialState = {
      loading: {
        objectone: false,
        objecttwo: false,
        objectthree: false,
      },
    };
    //action types
    const CHANGE_LOADING = 'CHANGE_LOADING';
    //action creators
    const changeLoading = (whatLoading) => ({
      type: CHANGE_LOADING,
      payload: whatLoading,
    });
    const reducer = (state, { type, payload }) => {
      if (type === CHANGE_LOADING) {
        return {
          ...state,
          loading: {
            ...state.loading,
            [payload]: !state.loading[payload],
          },
        };
      }
      return state;
    };
    //selectors
    const selectLoading = (state) => state.loading;
    const createSelectWhatLoading = (what) =>
      createSelector(
        [selectLoading],
        (loading) => loading[what]
      );
    //creating store with redux dev tools
    const composeEnhancers =
      window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
    const store = createStore(
      reducer,
      initialState,
      composeEnhancers(
        applyMiddleware(() => (next) => (action) =>
          next(action)
        )
      )
    );
    const Loading = React.memo(function Loading() {
      console.log('rendeing loading');
      const dispatch = useDispatch();
      const selectObjectOne = React.useMemo(
        () => createSelectWhatLoading('objectone'),
        []
      );
      const objectone = useSelector(selectObjectOne);
      return (
        <div>
          <p>objectone is {String(objectone)}</p>
          <button
            onClick={() => dispatch(changeLoading('objectone'))}
          >
            Change objectone
          </button>
          <button
            onClick={() =>
              dispatch(changeLoading('objecttwo'))
            }
          >
            Change objecttwo
          </button>
        </div>
      );
    });
    const App = () => {
      return <Loading />;
    };
    
    ReactDOM.render(
      <Provider store={store}>
        <App />
      </Provider>,
      document.getElementById('root')
    );
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/redux/4.0.5/redux.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react-redux/7.2.0/react-redux.min.js"></script>
    <div id="root"></div>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/reselect/4.0.0/reselect.min.js"></script>