Рассмотрим следующий пример:
// 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>
);
}
Я передаю реквизит и обратный вызов компоненту Form
. Когда вызывается обратный вызов, он обновляет состояние val
. Это состояние не используется в элементе <Form>
, но React по-прежнему повторно отображает его (Form rendered
выводится на консоль).
Это кажется ненужным. useCallback()
документация гласит:
useCallback вернет запомненную версию обратного вызова, которая изменяется только в случае изменения одной из зависимостей. Это полезно при передаче обратных вызовов оптимизированным дочерним компонентам, которые полагаются на равенство ссылок для предотвращения ненужных визуализаций.
Это довольно неоднозначно, но, похоже, предполагается, что компоненты можно настроить на обновление, только если какой-либо из их компонентов значения меняются, и если не использовать useCallback()
, это означает, что новая функция будет создаваться каждый раз, когда вызывается App()
, и, следовательно, Form
будет всегда обновляться.
Поскольку он говорит «оптимизированные» дочерние компоненты, по-видимому, это не поведение по умолчанию? Но почему? Зачем вам обновляться, если ни один из реквизитов не изменился? Разве весь смысл «оптимизации» компонента с помощью shouldComponentUpdate
заключается в том, что вы можете быть более расслабленным, чем «изменилась ли пропа?»