reactjs - 这如何解决反应钩子的状态关闭问题?
问题描述
我正在查看formik中的代码,这显然是解决反应钩子过时关闭问题的一种方法。
function useEventCallback<T extends (...args: any[]) => any>(fn: T): T {
const ref: any = React.useRef();
// we copy a ref to the callback scoped to the current state/props on each render
useIsomorphicLayoutEffect(() => {
ref.current = fn;
});
return React.useCallback(
(...args: any[]) => ref.current.apply(void 0, args),
[]
) as T;
}
我在其他库中看到过很多这种模式,但我不明白为什么这可以治愈它。
我不明白为什么创建一个ref
in auseEffect()
可以治愈任何事情。
它会使 linter 静音吗?
解决方案
该文档实际上指出:
无论哪种情况,我们都不推荐这种模式,只是为了完整性而在此处显示。相反,最好避免在深处传递回调。
假设我们无法避免传递回调,那么最简单的方法是为状态设置器使用回调:setSomeState(currentState=>....return something based on current state)
我不确定释放并发模式时这会如何表现,但这里有一个示例,说明如何使用对状态设置器的回调:
const ParentContainer = () => {
//list is created and maintained in parent
const [list, setList] = React.useState([
{ id: 1, val: true },
{ id: 2, val: true },
]);
//simplest way to get current list is to pass a callback
// to the state setter, now we can use useCallback without
// dependencies and never re create toggle during this life cycle
const toggle = React.useCallback(
id =>
setList(list =>
list.map(item =>
item.id === id
? { ...item, val: !item.val }
: item
)
),
[]
);
return Parent({ list, toggle });
};
const Parent = ({ list, toggle }) => (
<div>
{list.map(item => (
<ItemContainer
key={item.id}
item={item}
//every item gets the same toggle function
// reference to toggle never changes during Parent life cycle
toggle={toggle}
/>
))}
</div>
);
//Added memo to make ItemContainer a pure component
// as long as item or toggle never changes the (render) function
// will not be executed
// normally a pure component should not have side effects so don't
// do side effects in pure compnents (like mutating rendered var)
// it is only to visibly display how many times this function was
// called
const ItemContainer = React.memo(function ItemContainer({
item,
toggle: parentToggle,
}) {
const rendered = React.useRef(0);
//toggling item with id 1 will not increase render for
// other items (in this case item with id 2)
// this is because this is a pure component and this code
// will not be executed due to the fact that toggle or item
// never changed for item 2 when item 1 changed
rendered.current++;
const toggle = React.useCallback(
() => parentToggle(item.id),
[item.id, parentToggle]
);
return Item({ toggle, item, rendered });
});
const Item = ({ toggle, item, rendered }) => (
<div
onClick={() => toggle(item.id)}
style={{ cursor: 'pointer' }}
>
<div>{item.val ? '[X]' : '[-]'}</div>
<div>times rendered:{rendered.current}</div>
</div>
);
//render app
ReactDOM.render(
<ParentContainer />,
document.getElementById('root')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>
推荐阅读
- python - Bin使用累积和而不是python中的观察值
- c++ - 我们如何将数组的每个元素相乘并将每个乘积值存储在 c++ 中的数组中?
- python - 在一堆元组上做一个 for 循环?
- multithreading - 减少导致高 CPU 的特定进程的线程数
- json - 从字典中获取键
- java - 使用 Mockito 测试我的 SpringBoot 应用程序时如何从属性文件中传递变量?
- javascript - 如何将下拉选择更新为“已选择”onClick
- php - 如何获取 PHP 标签之间的函数的 HTML 内容?
- asp.net - AKS 中的 Web.API,许可证
- java - Spring JmsTemplate 目的地特定的消息持久性