I am trying to figure out a way to be able to memoize React components by specifying particular props.
For instance, if you use React.memo
— it memoizes the component based on all props.
What I am trying to achieve is being able to pass particular props as a dependency to a util (say, SuperMemo
) and the component will be memoized based on those props. The approach is very similar to what recompose — compose the component before export.
Here's an example code
import React from "react";
const isFunction = value =>
value &&
(Object.prototype.toString.call(value) === "[object Function]" ||
"function" === typeof value ||
value instanceof Function);
export const memo = (Comp, resolver) => {
if (isFunction(resolver)) {
const Memoized = props => {
const deps = resolver(props);
if (deps && deps.length) {
// eslint-disable-next-line react-hooks/rules-of-hooks
return React.useCallback(React.createElement(Comp, props), deps);
}
return React.createElement(Comp, props);
};
Memoized.displayName = `memoized(${Comp.name})`;
return Memoized;
}
return React.memo(Comp);
};
export default memo;
Here is how it will be used to compose components
import Todo from "./Todo";
import memo from "../memo";
export default memo(Todo, props => [props.text]);
I have a working codesandbox here — memo-deps
This is what I have observed —
React.useCallback
or any hook inside a conditional statement because React needs to know the order in which hooks are invoked and using it inside a conditional may mess up the order during runtimeReact.useCallback
works pretty neat in a conditional for my case as I know the order will remain the same during runtimeReact.useCallback
with lodash.memoize
and the end result will be pretty much the samelodash.memoize
or build a custom implementation of memoization while React.useCallback
pretty much does the work for meThis is where I am not sure what's happening (these are my questions) —
lodash.memoize
lodash.memoize
and React.useCallback
are not the same when I try to memoize a React componentReact.memo
is used (maybe to check prevProps vs newProps?)React.createElement
if not for React.useCallback
?The reason as to why I might want to do this —
I don't want to memoize handlers (closure with a value and event) every time I pass them to a component wrapped in React.memo
. I want to be able to declaratively write memoize dependencies for components.
The error "React hook 'useState' is called conditionally" occurs when we use the useState hook conditionally or after a condition that may return a value. To solve the error, move all React hooks above any conditionals that may return a value.
Don't call Hooks inside loops, conditions, or nested functions. Instead, always use Hooks at the top level of your React function, before any early returns. By following this rule, you ensure that Hooks are called in the same order each time a component renders.
Hook functions should only be called in function-based components and in custom hooks. Hook functions should not be called conditionally. This means they should not be called in if/else blocks, loops, or nested functions. This ensures that for any component, the same hooks are invoked in the same order on every render.
The useCallback hook is used when you have a component in which the child is rerendering again and again without need. Pass an inline callback and an array of dependencies. useCallback will return a memoized version of the callback that only changes if one of the dependencies has changed.
React.memo
accepts a function as the second parameter to do a custom props comparison.
By default it will only shallowly compare complex objects in the props object. If you want control over the comparison, you can also provide a custom comparison function as the second argument.
You can use that in your util function like this :
export const memoWithSecondParam = (Comp, deps = []) => {
return React.memo(Comp, (prevProps, nextProps) => {
return deps.every(d => prevProps[d] === nextProps[d])
});
};
And call it like this :
export default memoWithSecondParam(Todo, ["text"]);
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