Imagine you have a component expecting a function that renders a specific subtree in that component's hierarchy:
const Timeline = ({ renderDetails, ... }: { renderDetails: (rowData, sectionID, rowID) => React.Node }) => <div>{renderChildren(...)}</div>;
Is there a difference in using:
const MyComponent = React.memo(({ someProp }) => <someComponentHierarchyHere>...</someComponentHierarchyHere>);
const ParentComponent = ({ someProp }) => {
const renderChildren = useCallback(() => <MyComponent someProp={someProp} />, [someProp]);
return <Timeline renderChildren={renderChildren} />;
};
vs
const ParentComponent = ({ someProp }) => {
const MyComponent = useMemo(({ someProp }) => <someComponentHierarchyHere>...</someComponentHierarchyHere>);
const renderChildren = useCallback((rowData, sectionID, rowID) => <MyComponent someProp={someProp} />, [someProp]);
return <Timeline renderChildren={renderChildren} />;
};
or simply:
const ParentComponent = ({ someProp }) => {
const renderChildren = useCallback((rowData, sectionID, rowID) => <someComponentHierarchyHere>...</someComponentHierarchyHere>, [someProp]);
return <Timeline renderChildren={renderChildren} />;
};
? I'm specifically interested in the decisions React renderer will take, as well as whether the MyComponent component (or the hierarchy it inserts in the component tree) will be memoized.
In general, component definitions should always be static, so React.memo is the correct option here.
If your inner component is completely stateless, useMemo will appear to work, but you won't be able to use any hooks inside of the inner component, so for anything complex, you would have to do it the other way anyhow.
The reason for this is that, internally, React keeps track of what component is rendering at any given time, and that's how it determines what values are returned by useState and other hooks. Note that you're not using a dependencies array in your useMemo, meaning that it will be recreated on every render, so it's effectively pointless (the react hooks linting rules will warn you about this. However, even with an empty dependencies array, it still would not be safe to use hooks in it. A common misconception is that is useMemo with no dependencies will create a constant, but this is not the case. It's an optimization that usually prevents recalculation of values, but it's not guaranteed. Internally, React can choose to discard the memoized result of useMemo at any time. If that happened, it would randomly discard your state, so React prohibits it entirely.
import { useMemo, useState } from 'react';
export default function App() {
const MyComponent = useMemo(() => {
// This line errors:
// React Hook "useState" cannot be called inside a callback.
// React Hooks must be called in a React function component or a
// custom React Hook function. (react-hooks/rules-of-hooks) eslint
const [input, setInput] = useState('')
return (
<input value={input} onChange={(e) => setInput(e.target.text)} />
)
})
return <MyComponent />;
}
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