Norayr Ghukasyan
Norayr Ghukasyan

Reputation: 1408

React memoized component rerenders without props change

I have trouble with memoizing my component.

const Miscellaneous = () => {
    const dispatch = useDispatch<DispFn>();

    const brokers = useSelector(getSettingsMiscBrokers);
    const charterers = useSelector(getSettingsMiscCharterers);

    if (window.charterers) {
        console.log(window.charterers === charterers, 'check equality charterers');
    } else {
        window.charterers = charterers;
    }

    useEffect(() => {
        dispatch(fetchBrokersList());
    }, []);

    return (
        <AclShowSectionWrapper sectionName={CHARTER_SETTINGS}>
            <div className="fb" style={{ padding: '1rem 2rem 2rem 2rem' }}>
                <Row className="bigHorizontalScroll" grow noWrap>
                    <Col {...colGrid}>
                        <Charterers charterers={charterers} />
                    </Col>
                    <Col {...colGrid}>
                        <Brokers brokers={brokers} />
                    </Col>
                </Row>
            </div>
        </AclShowSectionWrapper>
    );
};

export const Charterers = React.memo((props: IProps) => {
    console.log('charterers');
    return (
        <div>{*some stuff here*}</div>
    );
});

An this is the selector function for charterers

 export const getSettingsMiscCharterers = createSelector(
  [(state: AppState) => state.shared.data.charterers],
  charterers => {
    if (!charterers || !charterers.payload) {
      return {
        ...charterers,
        payload: [],
        status: FETCH_STATUS.inProgress
      };
    }
    return charterers;
  }
);

So the problem is, that when I make a change in brokers table and that causes store changes only in brokers part, and this statement console.log(window.charterers === charterers, 'check equality charterers'); always is true, charterers get rendered every time.

Maybe I am missing something important here?

Upvotes: 1

Views: 695

Answers (1)

Incepter
Incepter

Reputation: 2948

This is because you don't provide any function to test on the props equality; so the shallow comparison is executed.

Thus, if your selector returns a new array/object (I presume it's the case especially if you are using array/object destructing to populate the selectors return value); the shallow equality will always return false; and then the component will re-render.

I recommend using isEqual from lodash to test on the equality

import { isEqual } from 'lodash';
Const MemoizedComponent = React.memo(Component, isEqual);

edit:

You are using the selector like this: useSelector(selectorCreator).

It should be useSelector(selectorCreator())

quoted from the docs

When using useSelector with an inline selector as shown above, a new instance of the selector is created whenever the component is rendered.

Besides, createSelector calls createSelectorCreator with the selector; that, returns a new function each time, that, also, calls the function with a new function every time

export const createSelector = /* #__PURE__ */ createSelectorCreator(defaultMemoize)
export function createSelectorCreator(memoize, ...memoizeOptions) {
  return (...funcs) => {
    let recomputations = 0
    const resultFunc = funcs.pop()
    const dependencies = getDependencies(funcs)

    const memoizedResultFunc = memoize(
      function () {
        recomputations++
        // apply arguments instead of spreading for performance.
        return resultFunc.apply(null, arguments)
      },
      ...memoizeOptions
    )

You can see the concrete implementation in github

Upvotes: 1

Related Questions