Последствия для внедрения двухстороннего связывания данных в Реагировании

Общим вопросом для новичков в React является то, почему двусторонняя привязка данных не является встроенной функцией, а обычный ответ включает объяснение однонаправленного потока данных, а также идею о том, что привязка данных с двусторонней привязкой не всегда желательна по соображениям производительности, Это второй момент, который я хотел бы разобраться более подробно.

В настоящее время я работаю над библиотекой форм для apollo-link-state (новый клиентский инструмент управления государственными средствами от Apollo). Концепция очень похожа на редукционную форму, за исключением использования apollo-link-state вместо redux в качестве менеджера состояний. (Обратите внимание, что состояние формы хранится отдельно от состояния объектов домена, хотя объект может быть необязательно использован для заполнения начального состояния формы.)

Когда пользователь вносит изменения в форму, библиотека немедленно обновляет хранилище с помощью обработчиков onChange. Я думал о том, чтобы позволить отдельным полям отказаться от этого поведения, если программист был обеспокоен производительностью, но потом я начал задаваться вопросом, когда это будет когда-либо реальная проблема с производительностью. Браузер будет oninput событие oninput несмотря ни на что, поэтому единственным соображением производительности, о котором я могу думать, является то, обновляется ли хранилище по типу пользователя. Конечно, есть дополнительные накладные расходы на выполнение мутации, а не просто вызов setState(), но это, по сути, всего лишь пара дополнительных вызовов функций. И пусть предположим, что я не использовал apollo, а просто вызвал функцию, которая напрямую обновляет какой-то глобальный магазин, - каков будет анализ производительности?

Мое мышление заключается в том, что если форма будет поддерживать немедленное обновление состояния формы по мере того, как пользователь вводит в одном поле, он может также сделать это для всех полей. Пользователь может вводить только одно поле за раз, и я не вижу преимущества того, что страница иногда быстрее (возможно, незначительна) с некоторыми полями, а иногда и с другими. Кроме того, моя библиотека позволяет потребителям использовать любые входные компоненты, которые они хотят, поэтому, если программист просто хочет меньше обновлений состояния, они могут просто написать компонент, который onChange событие React onChange или вместо этого использует собственное change или blur браузера.

Я что-то упустил? Есть ли еще одна причина, по которой пользователь моей библиотеки хотел бы игнорировать изменения для определенных полей, пока пользователь не отправит форму? Или, может быть, более полезным вариантом было бы игнорировать изменения во всей форме (до отправки)?

Вот базовая (значительно упрощенная) иллюстрация базовой концепции моего текущего подхода:

// defined in a globally-accessible module
const formState = {
    // This somehow causes any dependent form components to re-render
    // when state changes
    update(formName, updatedState) {
        ...
    }
}
export default formState

...
// UserForm.js:

export default class UserForm extends PureComponent {
    componentDidMount() {
        formState.userForm = {
            firstName: '',
            lastName: '',
        }
    }

    handleChange(e) {
        const { target } = e
        formState.update('userForm', { [target.name]: target.value })
    }

    //...

    render() {
        const { userForm } = formState
        return (
            <form onSubmit={this.handleSubmit}>
                <label for="name">Name</label>
                <input id="name" type="text" onChange={this.handleChange} value={userForm.name} />

                <label for="email">Email</label>
                <input id="email" type="email" onChange={this.handleChange} value={userForm.email} />
            </form>
        )
    }
}

Наконец, для полноты, я должен упомянуть, что в этом есть некоторые аспекты проектирования API. Отдельные компоненты ввода могут иметь несколько более простой дизайн, если я не предоставил возможность отказаться от автоматической двусторонней привязки. Я могу опубликовать детали, если кому-то это интересно.

Ответ 1

2-сторонние последствия привязки данных

Начиная с первой части вашего вопроса, есть две основные причины, по которым не реагировать на двухстороннее связывание данных:

  1. Единственный источник правды для изменения данных в приложении React, следовательно, меньше шансов на ошибки и более легкую отладку
  2. Эффективность

В React мы можем делиться состоянием между различными дочерними компонентами, поднимая состояние до общего родительского компонента. Когда обновляется общая часть состояния, все дочерние компоненты могут обновляться. Вот хороший пример из документов, связанных с формами.

Говоря о преимуществах производительности, двухсторонняя привязка данных в каком-либо другом контексте (например, AngularJS) работает с помощью наблюдателей, наблюдающих за различными элементами. Это звучит проще (и меньше кода, чем React one way data flow) для небольшого количества элементов, однако по мере роста числа компонентов/элементов вашего пользовательского интерфейса количество наблюдателей также увеличивается. Единственное изменение в этом случае заставляет многих наблюдателей запускаться, чтобы синхронизировать ситуацию. Это делает работу немного вялой. В случае React, поскольку есть только один способ передачи данных, легче определить, какие компоненты необходимо обновить.

Обработка обновлений состояния

Перейдя ко второй части вашего вопроса, ваша государственная библиотека предоставляет данные компонентам формы, заставляя любые зависимые компоненты обновляться при изменении состояния, сладко. Вот мои мысли:

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

Обновление магазина само по себе произойдет довольно быстро. JavaScript работает очень быстро, это обновления DOM, которые часто вызывают узкие места. Итак, если на одной странице нет сотен зависимых элементов формы, и все они обновляются, вы будете в порядке.

И пусть предположим, что я не использовал apollo, а просто вызвал функцию, которая напрямую обновляет какой-то глобальный магазин, - каков будет анализ производительности?

Я не думаю, что это будет иметь существенную разницу.

Мое мышление заключается в том, что если форма будет поддерживать немедленное обновление состояния формы по мере того, как пользователь вводит в одном поле, он может также сделать это для всех полей. Пользователь может вводить только одно поле за раз, и я не вижу возможности сделать страницу порой быстрее (возможно, небрежно) с некоторыми полями, а иногда и медленнее с другими.

Согласился с этим.

Моя библиотека позволяет потребителям использовать любые входные компоненты, которые они хотят, поэтому, если программист просто хочет меньше обновлений состояния, они могут просто написать компонент, который отлаживает событие React onChange или вместо этого использует собственное изменение или размытие браузера.

Я думаю, что большинство случаев использования будут решены простым input. Опять же, я не вижу преимущества производительности с меньшим количеством обновлений состояния здесь. Debounce может быть полезным, если, например, я запускаю вызов API на входе (и хочу ждать, пока пользователь перестанет печатать).

Есть ли еще одна причина, по которой пользователь моей библиотеки хотел бы игнорировать изменения для определенных полей, пока пользователь не отправит форму? Или, может быть, более полезным вариантом было бы игнорировать изменения для всей формы (до отправки)?

Я не вижу преимущества в игнорировании изменений для определенного поля или ожидания до отправки. С другой стороны, при использовании форм обычный пример использования, с которым я сталкиваюсь в реализации, - это проверка данных. Например,

  • предоставлять обратную связь пользователю, когда и когда он создает пароль
  • проверьте, действительно ли письмо
  • выполнять вызовы API, чтобы узнать, действительно ли имя пользователя и т.д.

Этим случаям понадобилось бы обновить состояние, когда пользователь печатает.

ТЛ; др

Вы должны быть в порядке с обновлением состояния при вводе пользователем. Если вы все еще обеспокоены производительностью, я бы предложил профилировать ваши компоненты, чтобы изолировать узкие места, если они есть :)