referring from the link. https://react-redux.js.org/next/api/hooks#performance
what i understand the benefit of useSelector
hook, is to avoid wrapper hell. Wrapper hell is happening due to the usage of connect
HOC. If we have to use React.memo
HOC with useSelector
due to perfomance reason, would it be better approach to simply use connect
HOC instead? Because in any case we would have to be in hell of wrappers. If the hell is not by connect
then would be by React.memo
.
Any one please explain the benefit of React.memo
over connect
.
I have been trying to get an answer for quite some time but the answers I got weren't clear. Although the theory in the Redux documentation isn't complicated: useSelector
uses strict equality ===
and connect uses shallow equality to determine. So in both cases, if you are "pulling" a primitive value from your Redux state (number, string, boolean) you will be having the same outcome. If values haven't changed none of the components will rerender. If you are "pulling" non-primitives (arrays or objects) and the values haven't changed for both cases (useSelector, connect), then the component that uses useSelector
will still rerender as of course [] === []
will always be false, as they are referencing different arrays, where as the connect
ed component will NOT rerender. Now in order to make useSelector
behave similarly and not rerender, you can do this:
const object = useSelector(state => state.object, shallowEqual)
You can import shallowEqual
from react-redux
. Or alternatively use a memoized version of that piece of state by using the reselect
library:
const makeGetObject = () => createSelector(state => state.object, object => object)
and add it to your selector such as: const object = useSelector(state => state.object, makeGetObject);
I have created this codesandbox when I was trying to get at the bottom of it (check the comments at the WithUseSelector
component): useSelector vs connect()
Well, first, interesting enough although React.memo is a HOC it does not create the same nesting as connect does. I have created a test code:
import React from "react";
import ReactDOM from "react-dom";
import {connect, Provider} from 'react-redux'
import { createStore } from 'redux'
import "./styles.css";
const MemoComponent = React.memo(function MyMemo() {
return <div>Memo</div>;
});
const ConnectedComponent = connect(null,null)(function MyConnected() {
return <div>ReduxConnectComponent</div>;
})
const store = createStore(()=>{},{})
function App() {
return (
<Provider store={store}>
<MemoComponent />
<ConnectedComponent/>
</Provider>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
And here is the structure rendered:
We can see that a content for connect is rendered deeper.
Second, the docs say:
by default useSelector() will do a reference equality comparison of the selected value when running the selector function after an action is dispatched, and will only cause the component to re-render if the selected value changed. However, unlike connect(), useSelector() does not prevent the component from re-rendering due to its parent re-rendering, even if the component's props did not change.
that means the component which useSelector will not be re-rendered when unrelated parts of the store change. And this is the most important part of the optimization. Whether optimizing with React.memo or not is now completely depends on your decision and in most cases, it simply is not needed. We use React.memo only in cases when the component is very expensive to render.
To summarize, connect wrapper was required to connect to the store. With useSelector we do not have to wrap anymore. We still need to wrap with React.memo in rare cases when we need to optimize some heavy components. The work of React.memo was also done by connect but in most cases, it was premature optimization.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With