Redux - ReactJS app does not rerender (although JSON.parse for new object)
Asked Answered
F

2

11

I have a ReactJS app with filters and use a RESET function for resetting those filters.

What I also use: Redux, Redux Persist and React-router-dom.

If I have a look at the Redux Devtools, it seems to work. But the App does not rerender correctly, a refresh (f5) is necessary.

What I want to achieve: override the configuredFilters object with the initialState object.

This is my root reducer:

const rootReducer = (state = initialState, action) => {
  let newState = state;
  if (action.type === 'RESET_FILTERS') {
    storage.removeItem('persist:configuredFilters');
    // eslint-disable-next-line no-param-reassign
    newState = { ...newState,
      configuredFilters: JSON.parse(JSON.stringify(initialState.configuredFilters))
    };
  }
  return appReducer(newState, action);
};

Here is the diff (I configured two countries before):

Diff after the RESET action has been called

Here is the object (initial status if the page is loaded):

The redux object configuredFilters

The components are created with this component:

/* eslint-disable max-len */
import React from 'react';
import { useSelector, shallowEqual, useDispatch } from 'react-redux';
import { Form, Select } from 'antd';
import PropTypes from 'prop-types';
import RenderTag from './RenderTag';
import * as Action from '../../store/configuredFilters/actions';

const propTypes = {
  data: PropTypes.shape({
    name: PropTypes.string.isRequired,
    label: PropTypes.string,
    placeholder: PropTypes.string,
    options: PropTypes.arrayOf(PropTypes.shape({})),
  }).isRequired,
};


const { Option } = Select;


const useData = () => {
  const dataFromRdx = useSelector(
    (state) => ({
      configuredFilters: state.configuredFilters,
    }),
    shallowEqual
  );
  return { dataFromRdx };
};

const FixedListSelect = ({
  data: {
    name, label, placeholder, options,
  },
}) => {
  const { dataFromRdx } = useData();

  const {
    configuredFilters: {
      data: {
        search: searchTerm,
      },
    },
  } = dataFromRdx;


  const dispatch = useDispatch();
  const dispatchFns = {
    setConfiguredFilters: (key, value) => {
      dispatch(Action.setConfiguredFilters(key, value));
    },
  };

  const setRdxVal = (id, currVal) => {
    dispatchFns.setConfiguredFilters(id, currVal);
  };

  const isFullTextSearchMode = (searchTerm && searchTerm.length);
  return (
    <Form.Item
      name={name}
      label={label}
      fieldKey={name}
    >
      <Select
        allowClear
        disabled={isFullTextSearchMode}
        showSearch
        tagRender={RenderTag}
        mode="multiple"
        placeholder={placeholder}
        optionFilterProp="children"
        filterOption={(input, option) => option.children.toLowerCase().includes(input.toLowerCase())}
        onChange={(currVal) => { setRdxVal(name, currVal); }}
      >
        {(options || []).map((el) => <Option data-filterid={el.val} key={el.val} value={el.val}>{el.label}</Option>)}
      </Select>
    </Form.Item>
  );
};
FixedListSelect.propTypes = propTypes;
export default FixedListSelect;

A call of this component:

<FixedListSelect data={{
          name: 'companies',
          label: t('companies'),
          placeholder: t('companies-placeholder'),
          options: companies,
        }}
        />

Can someone help or at least give a hint?

Fogbound answered 26/5, 2020 at 9:53 Comment(10)
A reducer wont trigger a new render of your component, it will just take care of updating the state. Can you show us the component that should re-render?Jada
@Jada yes, I added the component. Please see my edited post above.Fogbound
can you show appReducer tooStalkinghorse
This may be totally unconnected to the actual problem - but your rootReducer is not a pure function (it mutates an external variable called storage - I guess this is local storage?), and this is something you should never do. A reducer should take an action and the current state and returns the new state, with no side effects.Subliminal
You seem to be selecting an object with a deep state from the store. the second argument to useSelector is deepEqual be default, have you tried not replacing it with shallowEqual? state.configuredFilters is shallow state.configuredFilters.data.contries for example is deep and therefore won't cause a rerender unless you use deepEqual.Scriber
As much as I know there is no way to do this in this way. but what you can do is make another reducer function which will re-initialize the state. and call it whenever you need.Rhearheba
You are most def mutating state in your reducer. That causes your app not to respond to state updatesExhume
If possible please create a reproducible codesandbox of your issue. The scenario that you suggest shouldn't happen with the code that you showStalkinghorse
Why are you returning the appReducer call in the rootReducer?Marrakech
This is some real what on earth are you doing code. Can you include the code of appReducer?. Calling one reducer inside another is weird but not inherently a violation. Altering storage from a reducer is a side effect so that is not allowed. Deep cloning the initialState.configuredFilters is just weird. Why not return {...state, configuredFilters: initialState.configuredFilters}? Trying to override the behavior of redux persist from inside a reducer rather than through the redux persist setup feels wrong.Ingrown
C
2

In ReactJS you can not mutate the state directly. It is the same in Redux. You have to copy the existing info. Redux compares the current tree with new information if founds the difference then it updates the store and renders the new result. If mutating directly you will always refresh to have it working.

More info

Immutable Update Patterns

Calcicole answered 9/6, 2020 at 8:45 Comment(0)
M
0

Please clone last data. JSON.parse(JSON.stringify(data));

Memorabilia answered 21/11, 2022 at 2:39 Comment(1)
Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.Lectureship

© 2022 - 2025 — McMap. All rights reserved.