admin管理员组文章数量:1327487
I am trying to figure out a way to be able to memoize React ponents by specifying particular props.
For instance, if you use React.memo
— it memoizes the ponent 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 ponent will be memoized based on those props. The approach is very similar to what repose — pose the ponent 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 pose ponents
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 —
- I should not use
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 runtime - But
React.useCallback
works pretty neat in a conditional for my case as I know the order will remain the same during runtime - I am not using the hook inside the conditional statement during render, instead I am posing the ponent during export conditionally
- I am thinking about React ponents as plain JavaScript functions and trying to memoize it like how I would memoize a regular JavaScript function
- I could easily replace
React.useCallback
withlodash.memoize
and the end result will be pretty much the same - I don't want to use an external library like
lodash.memoize
or build a custom implementation of memoization whileReact.useCallback
pretty much does the work for me
This is where I am not sure what's happening (these are my questions) —
- React ponents are not really vanilla JavaScript functions and I cannot memoize them with
lodash.memoize
lodash.memoize
andReact.useCallback
are not the same when I try to memoize a React ponent- React executes the function before figuring out the render even when
React.memo
is used (maybe to check prevProps vs newProps?) - Is my implementation okay even though it breaks the rules of React? (use hook in a conditional statement)
- How else can I memoize a
React.createElement
if not forReact.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 ponent wrapped in React.memo
. I want to be able to declaratively write memoize dependencies for ponents.
I am trying to figure out a way to be able to memoize React ponents by specifying particular props.
For instance, if you use React.memo
— it memoizes the ponent 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 ponent will be memoized based on those props. The approach is very similar to what repose — pose the ponent 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 pose ponents
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 —
- I should not use
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 runtime - But
React.useCallback
works pretty neat in a conditional for my case as I know the order will remain the same during runtime - I am not using the hook inside the conditional statement during render, instead I am posing the ponent during export conditionally
- I am thinking about React ponents as plain JavaScript functions and trying to memoize it like how I would memoize a regular JavaScript function
- I could easily replace
React.useCallback
withlodash.memoize
and the end result will be pretty much the same - I don't want to use an external library like
lodash.memoize
or build a custom implementation of memoization whileReact.useCallback
pretty much does the work for me
This is where I am not sure what's happening (these are my questions) —
- React ponents are not really vanilla JavaScript functions and I cannot memoize them with
lodash.memoize
lodash.memoize
andReact.useCallback
are not the same when I try to memoize a React ponent- React executes the function before figuring out the render even when
React.memo
is used (maybe to check prevProps vs newProps?) - Is my implementation okay even though it breaks the rules of React? (use hook in a conditional statement)
- How else can I memoize a
React.createElement
if not forReact.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 ponent wrapped in React.memo
. I want to be able to declaratively write memoize dependencies for ponents.
-
There's no 'outside a ponent' in the code you posted. useCallback is used inside Memoized ponent. And it's misused because
React.createElement(Comp, props)
is not a function. – Estus Flask Commented May 7, 2019 at 9:14 -
Perhaps I used the wrong terminology. I meant that the
Memoized
is a wrapper over the real ponent and it is not affected by the render logic of the real ponent. – Dinesh Pandiyan Commented May 7, 2019 at 9:20 -
I was curious about
React.createElement(Comp, props)
as well. ShoulduseMemo
be used there instead ofuseCallback
? – Dinesh Pandiyan Commented May 7, 2019 at 9:23 -
2
it is not affected by the render logic of the real ponent - this is the reason why it's acceptable and doesn't break the rules of hooks. It should be
React.useMemo(() => React.createElement(Comp, props), ...)
then.React.useCallback(React.createElement(Comp, props), deps)
is workable but it's semantically incorrect and results in unnecessary createElement call. – Estus Flask Commented May 7, 2019 at 9:38 -
This has nothing to do with your question per se, but if you use Typescript, you don't have to waste time on implementing functions like
isFunction
. – Titulum Commented Dec 14, 2020 at 13:12
1 Answer
Reset to default 6 +25React.memo
accepts a function as the second parameter to do a custom props parison.
By default it will only shallowly pare plex objects in the props object. If you want control over the parison, you can also provide a custom parison 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"]);
本文标签:
版权声明:本文标题:javascript - Can the useCallback React hook be used conditionally even if it breaks the rules of hooks? - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1742205636a2432761.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论