Понимание правила lint React Hooks 'Exustive-deps'

Мне трудно понять правило линта "исчерпывающая глубина".

Я уже читал этот пост и этот пост, но не нашел ответа.

Вот простой компонент React с проблемой ворса:

const MyCustomComponent = ({onChange}) => {
    const [value, setValue] = useState('');

    useEffect(() => {
        onChange(value);
    }, [value]);

    return (
        <input 
           value={value} 
           type='text' 
           onChange={(event) => setValue(event.target.value)}>
        </input>
    )
} 

Мне нужно добавить onChange к useEffectмассив зависимостей. Но в моем пониманииonChange никогда не изменится, поэтому его там не должно быть.

Обычно мне это удается так:

const MyCustomComponent = ({onChange}) => {
    const [value, setValue] = useState('');

    const handleChange = (event) => {
        setValue(event.target.value);
        onChange(event.target.value)
    }

    return (
        <input value={value} type='text' onChange={handleChange}></input>
    )
} 

Почему ворс? Есть ли четкое объяснение правила lint для первого примера?

Или я не должен использовать useEffectВот? (Я новичок с крючками)

2 ответа

Решение

Причина, по которой правило линтера хочет onChange войти в useEffect крючок потому что это возможно для onChange для переключения между визуализацией, а правило lint предназначено для предотвращения такого рода ссылок на "устаревшие данные".

Например:

const MyParentComponent = () => {
    const onChange = (value) => { console.log(value); }

    return <MyCustomComponent onChange={onChange} />
}

Каждый рендер MyParentComponent пройдет другой onChange функция для MyCustomComponent.

В вашем конкретном случае вам, вероятно, все равно: вы хотите только позвонить onChange когда значение меняется, а не когда onChangeфункция меняется. Однако это не ясно из того, как вы используетеuseEffect.


Корень здесь в том, что ваш useEffect несколько унидиоматичен.

useEffectлучше всего использовать для побочных эффектов, но здесь вы используете его как своего рода концепцию "подписки", например: "делать X, когда Y изменяется". Функционально это работает, благодаря механикеdeps массив (хотя в этом случае вы также вызываете onChange на начальном рендере, что, вероятно, нежелательно), но не по назначению.

Вызов onChange на самом деле это не побочный эффект, это просто эффект срабатывания onChange мероприятие для <input>. Так что я думаю, что ваша вторая версия, которая называет обаonChange а также setValue вместе более идиоматичен.

Если бы были другие способы установки значения (например, кнопка очистки), постоянно нужно помнить о вызове onChange может быть утомительным, поэтому я мог бы написать это как:

const MyCustomComponent = ({onChange}) => {
    const [value, _setValue] = useState('');

    // Always call onChange when we set the new value
    const setValue = (newVal) => {
        onChange(newVal);
        _setValue(newVal);
    }

    return (
        <input value={value} type='text' onChange={e => setValue(e.target.value)}></input>
        <button onClick={() => setValue("")}>Clear</button>
    )
}

Но на данный момент это косяк.

Основное назначение exhaustive-deps предупреждение состоит в том, чтобы не дать разработчикам пропустить зависимости внутри своего эффекта и потерять некоторое поведение.

Дэн Абрамов, разработчик ядра Facebook, настоятельно рекомендует оставить это правило включенным.

В случае передачи функций в качестве зависимостей в React FAQ есть специальная глава:

https://reactjs.org/docs/hooks-faq.html#is-it-safe-to-omit-functions-from-the-list-of-dependencies

tl;dr

Если вам нужно поместить функцию в массив зависимостей:

  • Поместите функцию за пределы вашего компонента, чтобы вы были уверены, что ссылка не будет изменяться при каждой визуализации.
  • Если можете, вызовите функцию вне вашего эффекта и просто используйте результат как зависимость.
  • Если функция должна быть объявлена ​​в области видимости вашего компонента, вы должны запомнить ссылку на функцию с помощью useCallbackкрючок. Ссылка будет изменена только в случае изменения зависимостей функции обратного вызова.
Другие вопросы по тегам