Reputation: 1408
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
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