请考虑以下示例:
// Form.tsx
import React from "react";
interface FormProps {
name: string;
onClick: (n: string) => void;
}
export function Form(props: FormProps) {
console.log("Form rendered");
return (
<input type="button" onClick={() => props.onClick(props.name)} value={`Hello ${props.name}`}/>
);
}
// App.tsx
import React, { useState, useCallback } from 'react';
import './App.css';
import { Form } from './Form';
interface AppState {
val: number;
}
interface AppProps {
}
export function App(props: AppProps) {
const [val, setVal] = useState(1);
const onClick = useCallback(
(n: string) => {
console.log(val);
setVal(val + 1);
},
[val],
);
return (
<div>
<p>Value: {val}</p>
<Form name="foo" onClick={onClick}/>
</div>
);
}
我将 prop 和回调传递给Form
组件。调用回调时,它会更新val
状态。该状态不会在<Form>
元素中使用,但 React 仍然会重新渲染它(Form rendered
打印到控制台(。
这似乎没有必要。useCallback()
文档说:
useCallback 将返回回调的记忆版本,仅当其中一个依赖项发生更改时才会更改。当将回调传递给依赖于引用相等以防止不必要的渲染的优化子组件时,这很有用
这是相当模棱两可的,但它似乎表明组件可以配置为仅在其任何 prop 值更改时才更新,并且不使用useCallback()
意味着每次调用App()
时都会创建一个新功能,因此Form
将始终更新。
既然它说"优化"的子组件,大概这不是默认行为吗?但是为什么?如果道具都没有改变,你为什么要更新?通过shouldComponentUpdate
"优化"组件的全部意义难道不是比"道具是否改变了"更放松吗?
当你将状态(val
(作为依赖传递给useCallback()
时,当val
发生变化时,会生成一个新函数。这会导致子组件重新呈现。
若要跳过将val
作为依赖项包含在内,同时在要更新它时仍具有先前状态的值的需要,请使用更新程序函数:
如果使用以前的状态计算新状态,则可以传递 函数来设置状态。该函数将接收上一个值, 并返回更新的值。
例:
const onClick = useCallback(
(n: string) => {
setVal(v => v + 1);
},
[],
);