javascriptreactjsreducers

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


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?


Solution

  • 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