从react钩子返回回调的最佳方式



我想制作一个自定义挂钩,对某个元素的点击事件做出反应,并进行一些状态更新:

function useCustom() {
// a sample state within the hook
const [counter, setCounter] = useState(0);
// the callback I'm talking about
const onClick = event => {
console.log(`Hey! counter is ${counter}`);
setCounter(val=>val+1);
}
// returning the callback to be used in the component
return {onClick};
}

现在使用这个钩子的组件

function MyComp(props) {
const cbs = useCustom();
// component onclick handler
const onClick = event => cbs.onClick(event);
return <button onClick={onClick}>Click me!</button>
}

这种方法存在一些问题。首先,钩子中的callabck函数onClick以某种方式连接到这个钩子的第一个闭包,这意味着其中counter的值总是0(每次单击时它都会记录"嘿!counter是0"(。好吧,为了解决这个问题,我需要使用useRef。。。我知道如何做到这一点,但我在这里主要担心的是,这个回调在某种程度上与钩子的早期(和过时(闭包相连,因此它保持了这个闭包(及其所有变量(的有效性(而不是垃圾收集(,因此它是一种内存泄漏(??我在这里犯了一个错误吗(-所以从钩子返回这样的回调通常是一个好做法吗???

一个似乎更具反应性的替代解决方案是使用状态变量而不是钩子中的回调,并返回该状态的设置状态函数:

function useCustom() {
// a sample state within the hook
const [counter, setCounter] = useState(0);

// state to hold the last click event
const [clickEvent, setClickEvent] = useState(null);
// replacing the callback with an effect callback
useEffect(() => {
console.log(`Hey! counter is ${counter}`);
setCounter(val=>val+1);
}, [clickEvent]);
// returning the set state function instead of callback
return {setClickEvent};
}

然后在组件中:

function MyComp(props) {
const cbs = useCustom();
// component onclick handler
const onClick = event => cbs.setClickEvent(event);
return <button onClick={onClick}>Click me!</button>
}

注意,这样做的缺点是,我们将在单击时对钩子进行两次渲染(即执行((一次是因为setClickEvent,另一次是在clickEvent…的效果内的setCounter(。这可能是与回调方法相比的折衷。

那么,哪一个获胜呢?callback方法或setState方法,或者??

setState是异步的。您可以在React:的文档中看到

  • https://reactjs.org/docs/faq-state.html#why-setstate给我的值是错误的吗js
  • https://reactjs.org/docs/faq-state.html#when-是setstate异步的

您可以通过将useEffect添加到counter来记录该值,该值将在计数器更改时触发:

React.useEffect(() => {
console.log(`Hey! counter is ${counter}`); // <= log when counter changes
}, [counter]);

工作示例:

function useCustom() {
// a sample state within the hook
const [counter, setCounter] = React.useState(0);
// the callback I'm talking about
const onClick = (event) => {
setCounter((val) => val + 1);
};
React.useEffect(() => {
console.log(`Hey! counter is ${counter}`);
}, [counter]);
// returning the callback to be used in the component
return { onClick };
}
function App() {
const cbs = useCustom();
// component onclick handler
const onClick = (event) => cbs.onClick(event);
return <button onClick={onClick}>Click me!</button>;
}
ReactDOM.render(<App />, document.getElementById("react"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="react"></div>

最新更新