admin管理员组文章数量:1287631
Basically this:
function MyComponent() {
let [count, setCount] = useState(1)
let memo = useMyMemo(() => new MyClass)
return <div onClick={update}>{count}</div>
function update() {
setCount(count + 1)
}
}
function useMyMemo(fn) {
// what to do here?
}
class MyClass {
}
I would like for useMyMemo
to only return 1 instance of the class per ponent instance. How do I set this up to implement it without resorting to using any of the existing React hooks? If it's not possible without the React hooks, they why not? If it is possible only through accessing internal APIs, how would you do that?
As a bonus it would be helpful to know how it could be passed property dependencies, and how it would use that to figure out if the memo should be invalidated.
Basically this:
function MyComponent() {
let [count, setCount] = useState(1)
let memo = useMyMemo(() => new MyClass)
return <div onClick={update}>{count}</div>
function update() {
setCount(count + 1)
}
}
function useMyMemo(fn) {
// what to do here?
}
class MyClass {
}
I would like for useMyMemo
to only return 1 instance of the class per ponent instance. How do I set this up to implement it without resorting to using any of the existing React hooks? If it's not possible without the React hooks, they why not? If it is possible only through accessing internal APIs, how would you do that?
As a bonus it would be helpful to know how it could be passed property dependencies, and how it would use that to figure out if the memo should be invalidated.
Share Improve this question asked Aug 10, 2019 at 10:44 Lance PollardLance Pollard 79.5k98 gold badges330 silver badges607 bronze badges 1-
1
Not an answer, but you'll need to dig into React internals to do this. Hooks work because React knows what ponent function it's currently calling.
useState
and such use that internal information to do their work (see theresolveDispatcher
calls throughout theReactHooks.js
file), they're basically wrappers for calls on a "dispatcher" object. One such dispatcher is implemented here. – T.J. Crowder Commented Aug 10, 2019 at 10:55
2 Answers
Reset to default 6I think you're describing how useMemo
already works, assuming you pass []
as the dependencies parameter. It should already create one instance of MyClass
per instance of MyComponent
. However, the documentation says that future versions of useMemo
will not guarantee that the value is only called once, so you might want to try useRef
.
const memo = useRef(null);
if (!memo.current) {
memo.current = new MyClass()
}
If you want it to create a new instance of MyClass
when dependencies change, you'll have to either use useMemo
(and accept the chance that the value might be invalidated occasionally), or do your own shallow parison against the previous value. Something like this:
const useMyMemo = (create, dependencies) => {
const val = React.useRef(create());
const prevDependencies = React.useRef([]);
if (!shallowEquals(dependencies, prevDependencies.current)) {
val.current = create();
prevDependencies.current = [...dependencies];
}
return val;
};
I'll leave the implementation of shallowEquals
to you, but I believe lodash has one.
Now you really asked for a function that doesn't use any React hooks, and mine uses useRef
. If you don't want useRef
, you can create your own simple memoization function that always returns the same pointer regardless of changes to .current
.
Simple mock implementation of useMemo
using useRef
.
Note: You can shallowCompare or deepCompare to pute
isChanged
based on your need. Example if dependencies areobjects
.
/* File: useMemo.js */
import { useRef } from "react";
export function useMemo(callback, deps) {
const mPointer = useRef([deps]); // Memory pointer (Make sure to read useRef docs)
const isChanged = mPointer.current[0].some(
// If dependencies changed
(item, index) => item !== deps[index]
);
if (mPointer.current.length === 1 || isChanged) {
// If first time or changed, pute and store it
mPointer.current = [deps, callback()];
}
return mPointer.current[1];
}
See CodeSandbox demo.
Read: useRef returns a mutable ref object whose
.current
property is initialized to the passed argument (initialValue). The returned object will persist for the full lifetime of the ponent.
Pure javascript mock using closure
- [ For educational purpose ONLY! ]
/* File: useMemo.js */
function memo() {
const stack = {};
return (callback, deps) => {
/* Creating hashkey with dependencies + function source without whitespaces */
const hash = deps.join("/") + "/" + callback.toString().replace(/\s+/g, "");
if (!stack[hash]) {
stack[hash] = callback();
}
return stack[hash];
};
};
export const useMemo = memo();
See CodeSandbox demo.
Idea explaination : With above code trying to create a unique
hash
by bining dependencies value & function source usingtoString
, removing spaces in string to make hash as unique as possible. Using this as a key in thehashmap
to store the data and retrieve when called. The above code is buggy, for example callback result that are cached are not removed when ponent unmount's. AddinguseEffect
and return clean-up function should fix this during unmount. Then this solution bees tightly coupled with react framework
Hope this helps. Feedback is weled to improve the answer!
本文标签: javascriptHow to implement your own useMemo from Scratchfor ReactStack Overflow
版权声明:本文标题:javascript - How to implement your own useMemo from Scratch, for React - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1741314926a2371856.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论