React hooks useCallback 与循环内的参数
React hooks useCallback with parameters inside loop
我正在尝试了解挂钩功能,但我似乎不太明白应该如何正确使用函数 useCallback
。据我对钩子规则的理解,我应该将它们称为顶级而不是逻辑(例如循环)。这让我很困惑我应该如何在循环渲染的组件上实现 useCallback
。
看看下面的示例代码片段,我在其中创建了 5 个按钮,其中的 onClick
处理程序将按钮的编号打印到控制台。
const Example = (props) => {
const onClick = (key) => {
console.log("You clicked: ", key);
};
return(
<div>
{
_.times(5, (key) => {
return (
<button onClick={() => onClick(key)}>
{key}
</button>
);
})
}
</div>
);
};
console.log("hello there");
ReactDOM.render(<Example/>, document.getElementById('root'));
<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.11/lodash.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id='root'>
</div>
现在,由于我在 <button onClick={() => onClick(key)}>
中使用了箭头函数,因此每次示例呈现时它都会创建一个新的函数实例,我这样做是因为我希望 onClick
函数知道哪个按钮叫它。我想我可以通过使用 useCallback
的新反应钩子功能来防止这种情况,但是如果我将它应用于 const onClick
那么它仍然会在每次渲染时创建一个新实例,因为需要内联箭头函数给出 key
参数,据我所知,我不允许将它应用于循环内的渲染(特别是如果循环顺序可能会改变吗?)。
那么,在这种情况下,我将如何实施 useCallback
同时保持相同的功能?有可能吗?
这里的简单回答是,您可能不应该在这里使用 useCallback
。 useCallback
的要点是将相同的函数实例传递给优化组件(例如 PureComponent
或 React.memo
化组件)以避免不必要的重新渲染。
在这种情况下(或者大多数情况下,我怀疑)你没有处理优化的组件,所以没有真正的理由像 useCallback
.
那样记忆回调
假设记忆很重要,不过,这里最好的解决方案可能是使用单个函数而不是五个函数:不是每个按钮的唯一函数通过闭包携带 key
,您可以附加key
到元素:
<button data-key={key}>{key}</button>
然后在单击处理程序中从 event.target.dataset["key"]
中读取密钥:
const Example = (props) => {
// Single callback, shared by all buttons
const onClick = React.useCallback((e) => {
// Check which button was clicked
const key = e.target.dataset["key"]
console.log("You clicked: ", key);
}, [/* dependencies */]);
return(
<div>
{
_.times(5, (key) => {
return (
<button data-key={key} onClick={onClick}>
{key}
</button>
);
})
}
</div>
);
};
console.log("hello there");
ReactDOM.render(<Example/>, document.getElementById('root'));
<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.11/lodash.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>
<div id='root'>
</div>
综上所述,可以在一个钩子中记忆多个函数。 useCallback(fn, deps)
等同于useMemo(() => fn, deps)
,useMemo
可以一次记忆多个函数:
const clickHandlers = useMemo(() => _.times(5, key =>
() => console.log("You clicked", key)
), [/* dependencies */]);
const Example = (props) => {
const clickHandlers = React.useMemo(() => _.times(5, key =>
() => console.log("You clicked", key)
), [/* dependencies */])
return(
<div>
{
_.times(5, (key) => {
return (
<button onClick={clickHandlers[key]}>
{key}
</button>
);
})
}
</div>
);
};
console.log("hello there");
ReactDOM.render(<Example/>, document.getElementById('root'));
<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.11/lodash.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>
<div id='root'>
</div>
也许在某些情况下这是有用的,但在这种情况下,我要么不去管它(而不用担心优化),要么为每个按钮使用一个处理程序。
我正在尝试了解挂钩功能,但我似乎不太明白应该如何正确使用函数 useCallback
。据我对钩子规则的理解,我应该将它们称为顶级而不是逻辑(例如循环)。这让我很困惑我应该如何在循环渲染的组件上实现 useCallback
。
看看下面的示例代码片段,我在其中创建了 5 个按钮,其中的 onClick
处理程序将按钮的编号打印到控制台。
const Example = (props) => {
const onClick = (key) => {
console.log("You clicked: ", key);
};
return(
<div>
{
_.times(5, (key) => {
return (
<button onClick={() => onClick(key)}>
{key}
</button>
);
})
}
</div>
);
};
console.log("hello there");
ReactDOM.render(<Example/>, document.getElementById('root'));
<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.11/lodash.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id='root'>
</div>
现在,由于我在 <button onClick={() => onClick(key)}>
中使用了箭头函数,因此每次示例呈现时它都会创建一个新的函数实例,我这样做是因为我希望 onClick
函数知道哪个按钮叫它。我想我可以通过使用 useCallback
的新反应钩子功能来防止这种情况,但是如果我将它应用于 const onClick
那么它仍然会在每次渲染时创建一个新实例,因为需要内联箭头函数给出 key
参数,据我所知,我不允许将它应用于循环内的渲染(特别是如果循环顺序可能会改变吗?)。
那么,在这种情况下,我将如何实施 useCallback
同时保持相同的功能?有可能吗?
这里的简单回答是,您可能不应该在这里使用 useCallback
。 useCallback
的要点是将相同的函数实例传递给优化组件(例如 PureComponent
或 React.memo
化组件)以避免不必要的重新渲染。
在这种情况下(或者大多数情况下,我怀疑)你没有处理优化的组件,所以没有真正的理由像 useCallback
.
假设记忆很重要,不过,这里最好的解决方案可能是使用单个函数而不是五个函数:不是每个按钮的唯一函数通过闭包携带 key
,您可以附加key
到元素:
<button data-key={key}>{key}</button>
然后在单击处理程序中从 event.target.dataset["key"]
中读取密钥:
const Example = (props) => {
// Single callback, shared by all buttons
const onClick = React.useCallback((e) => {
// Check which button was clicked
const key = e.target.dataset["key"]
console.log("You clicked: ", key);
}, [/* dependencies */]);
return(
<div>
{
_.times(5, (key) => {
return (
<button data-key={key} onClick={onClick}>
{key}
</button>
);
})
}
</div>
);
};
console.log("hello there");
ReactDOM.render(<Example/>, document.getElementById('root'));
<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.11/lodash.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>
<div id='root'>
</div>
综上所述,可以在一个钩子中记忆多个函数。 useCallback(fn, deps)
等同于useMemo(() => fn, deps)
,useMemo
可以一次记忆多个函数:
const clickHandlers = useMemo(() => _.times(5, key =>
() => console.log("You clicked", key)
), [/* dependencies */]);
const Example = (props) => {
const clickHandlers = React.useMemo(() => _.times(5, key =>
() => console.log("You clicked", key)
), [/* dependencies */])
return(
<div>
{
_.times(5, (key) => {
return (
<button onClick={clickHandlers[key]}>
{key}
</button>
);
})
}
</div>
);
};
console.log("hello there");
ReactDOM.render(<Example/>, document.getElementById('root'));
<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.11/lodash.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>
<div id='root'>
</div>
也许在某些情况下这是有用的,但在这种情况下,我要么不去管它(而不用担心优化),要么为每个按钮使用一个处理程序。