Исправьте JSX и имя компонента с заглавной буквы. Подписку создавайте в эффекте с cleanup; не запускайте новый бесконечный интервал на каждый клик и каждый рендер.
Ревью и исправление React‑компонента: корректное использование useLayoutEffect и useCallback
Исправьте JSX и имя компонента с заглавной буквы. Подписку создавайте в эффекте с cleanup; не запускайте новый бесконечный интервал на каждый клик и каждый рендер.
Короткий ответ
Что ответить на собеседовании
Подробный разбор
Ответ с пояснениями
Условие
Необходимо провести проверку и доработку React‑компонента, выявив ошибки в применении хуков useLayoutEffect и useCallback, а также избавиться от потенциальных утечек памяти, лишних обработчиков событий и некорректного рендера JSX.
const reviewComponent = () => {
const [counter, setCounter] = React.useState(1);
const [list, setList] = React.useState([{ id: 1 }]);
React.useLayoutEffect(() => {
document.addEventListener("click", () => {
setInterval(() => console.log(counter), 1000);
});
});
const handleClick = React.useCallback(() => {
setCounter(counter + 1);
setList([...list, { id: counter + 1 }]);
});
return (
<React.Fragment>
<ul>
(list.map((item) => (
<li>{item.id)</li>
))}
</ul>
<button onClick={() => handleClick()}>add one</button>
</React.Fragment>
}
Ответ
Исправьте JSX и имя компонента с заглавной буквы. Подписку создавайте в эффекте с cleanup; не запускайте новый бесконечный интервал на каждый клик и каждый рендер.
Один вариант явной логики: первый клик включает логирование счётчика, кнопка добавляет элемент.
function ReviewComponent() {
const [state, setState] = React.useState({ counter: 1, list: [{ id: 1 }] });
const [logging, setLogging] = React.useState(false);
React.useEffect(() => {
const start = () => setLogging(true);
document.addEventListener('click', start);
return () => document.removeEventListener('click', start);
}, []);
React.useEffect(() => {
if (!logging) return;
const timer = setInterval(() => console.log(state.counter), 1000);
return () => clearInterval(timer);
}, [logging, state.counter]);
const handleClick = React.useCallback(() => setState(previous => {
const counter = previous.counter + 1;
return { counter, list: [...previous.list, { id: counter }] };
}), []);
return <>
<ul>{state.list.map(item => <li key={item.id}>{item.id}</li>)}</ul>
<button onClick={handleClick}>add one</button>
</>;
}
Интервал пересоздаётся при изменении счётчика и всегда очищается. useLayoutEffect не нужен, потому что измерений DOM до отрисовки нет. Связанные counter/list обновляются одним чистым updater.