将 HTML 元素的 ref 传递给自定义挂钩
Passing a ref to an HTML element to a custom hook
假设我有一个自定义挂钩,我将使用它来将点击事件侦听器添加到 HTML 元素中。
我用 const buttonRef = useRef(null);
创建了 ref,所以第一次渲染时的值为 null。 ref 值仅在渲染方法的最后分配,在我的自定义挂钩已被调用的位置。
因此,在第一次渲染时,我的自定义挂钩没有任何可添加事件侦听器的内容。
我最终不得不在我的自定义挂钩可以第二次 运行 之前更新组件,并最终将事件侦听器添加到元素。我得到以下行为:
问题:
如何解决这个问题?我真的需要强制更新我的组件才能将 ref 发送到自定义挂钩吗?因为我只能在顶层调用钩子和自定义钩子(钩子规则),所以不允许像下面这样的东西。
useEffect(() => {
useMyHook();
});
App.js
function App() {
const buttonRef = useRef(null);
const hookValue = useMyHook(buttonRef.current);
const [forceUpdate, setForceUpdate] = useState(false);
return (
<div>
<button onClick={() => setForceUpdate(prevState => !prevState)}>
Update Component
</button>
<button ref={buttonRef}>Update Hook</button>
{"This is hook returned value: " + hookValue}
</div>
);
}
useMyHook.js(自定义挂钩)
import { useEffect, useState } from "react";
function useMyHook(element) {
const [myHookState, setMyHookState] = useState(0);
console.log("Inside useMyhook...");
console.log("This is the element received: " + element);
useEffect(() => {
console.log("Inside useMyhook useEffect...");
function onClick() {
setMyHookState(prevState => prevState + 1);
}
if (element !== null) {
element.addEventListener("click", onClick);
}
return () => {
console.log("Inside useMyhook useEffect return...");
if (element !== null) {
element.removeEventListener("click", onClick);
}
};
});
return myHookState;
}
export default useMyHook;
解决方案非常简单,您只需将 ref 传递给自定义挂钩而不是 ref.current
,因为当将 ref 分配给DOM 并且自定义挂钩中的 useEffect 被触发 post 第一个渲染,因此 buttonRef.current
将引用 DOM 节点
useMyHook.js
import { useEffect, useState } from "react";
function useMyHook(refEl) {
const [myHookState, setMyHookState] = useState(0);
console.log("Inside useMyhook...");
console.log("This is the element received: ", refEl);
useEffect(() => {
const element = refEl.current;
console.log("Inside useMyhook useEffect...");
function onClick() {
console.log("click");
setMyHookState(prevState => prevState + 1);
}
console.log(element);
if (element !== null) {
element.addEventListener("click", onClick);
}
return () => {
console.log("Inside useMyhook useEffect return...");
if (element !== null) {
element.removeEventListener("click", onClick);
}
};
}, []);
return myHookState;
}
export default useMyHook;
index.js
function App() {
const buttonRef = useRef(null);
const hookValue = useMyHook(buttonRef);
const [forceUpdate, setForceUpdate] = useState(false);
return (
<div>
<button onClick={() => setForceUpdate(prevState => !prevState)}>
Update Component
</button>
<button ref={buttonRef}>Update Hook</button>
{"This is hook returned value: " + hookValue}
</div>
);
}
假设我有一个自定义挂钩,我将使用它来将点击事件侦听器添加到 HTML 元素中。
我用 const buttonRef = useRef(null);
创建了 ref,所以第一次渲染时的值为 null。 ref 值仅在渲染方法的最后分配,在我的自定义挂钩已被调用的位置。
因此,在第一次渲染时,我的自定义挂钩没有任何可添加事件侦听器的内容。
我最终不得不在我的自定义挂钩可以第二次 运行 之前更新组件,并最终将事件侦听器添加到元素。我得到以下行为:
问题:
如何解决这个问题?我真的需要强制更新我的组件才能将 ref 发送到自定义挂钩吗?因为我只能在顶层调用钩子和自定义钩子(钩子规则),所以不允许像下面这样的东西。
useEffect(() => {
useMyHook();
});
App.js
function App() {
const buttonRef = useRef(null);
const hookValue = useMyHook(buttonRef.current);
const [forceUpdate, setForceUpdate] = useState(false);
return (
<div>
<button onClick={() => setForceUpdate(prevState => !prevState)}>
Update Component
</button>
<button ref={buttonRef}>Update Hook</button>
{"This is hook returned value: " + hookValue}
</div>
);
}
useMyHook.js(自定义挂钩)
import { useEffect, useState } from "react";
function useMyHook(element) {
const [myHookState, setMyHookState] = useState(0);
console.log("Inside useMyhook...");
console.log("This is the element received: " + element);
useEffect(() => {
console.log("Inside useMyhook useEffect...");
function onClick() {
setMyHookState(prevState => prevState + 1);
}
if (element !== null) {
element.addEventListener("click", onClick);
}
return () => {
console.log("Inside useMyhook useEffect return...");
if (element !== null) {
element.removeEventListener("click", onClick);
}
};
});
return myHookState;
}
export default useMyHook;
解决方案非常简单,您只需将 ref 传递给自定义挂钩而不是 ref.current
,因为当将 ref 分配给DOM 并且自定义挂钩中的 useEffect 被触发 post 第一个渲染,因此 buttonRef.current
将引用 DOM 节点
useMyHook.js
import { useEffect, useState } from "react";
function useMyHook(refEl) {
const [myHookState, setMyHookState] = useState(0);
console.log("Inside useMyhook...");
console.log("This is the element received: ", refEl);
useEffect(() => {
const element = refEl.current;
console.log("Inside useMyhook useEffect...");
function onClick() {
console.log("click");
setMyHookState(prevState => prevState + 1);
}
console.log(element);
if (element !== null) {
element.addEventListener("click", onClick);
}
return () => {
console.log("Inside useMyhook useEffect return...");
if (element !== null) {
element.removeEventListener("click", onClick);
}
};
}, []);
return myHookState;
}
export default useMyHook;
index.js
function App() {
const buttonRef = useRef(null);
const hookValue = useMyHook(buttonRef);
const [forceUpdate, setForceUpdate] = useState(false);
return (
<div>
<button onClick={() => setForceUpdate(prevState => !prevState)}>
Update Component
</button>
<button ref={buttonRef}>Update Hook</button>
{"This is hook returned value: " + hookValue}
</div>
);
}