Использовать состояние или ссылки в компонентах формы React.js?

116

Я начинаю с React.js и хочу создать простую форму, но в документации я нашел два способа сделать это.

Первый из них использует Refs :

var CommentForm = React.createClass({
  handleSubmit: function(e) {
    e.preventDefault();
    var author = React.findDOMNode(this.refs.author).value.trim();
    var text = React.findDOMNode(this.refs.text).value.trim();
    if (!text || !author) {
      return;
    }
    // TODO: send request to the server
    React.findDOMNode(this.refs.author).value = '';
    React.findDOMNode(this.refs.text).value = '';
    return;
  },
  render: function() {
    return (
      <form className="commentForm" onSubmit={this.handleSubmit}>
        <input type="text" placeholder="Your name" ref="author" />
        <input type="text" placeholder="Say something..." ref="text" />
        <input type="submit" value="Post" />
      </form>
    );
  }
});

А второй использует состояние внутри компонента React:

var TodoTextInput = React.createClass({
  getInitialState: function() {
    return {
      value: this.props.value || ''
    };
  },

  render: function() /*object*/ {
    return (
      <input className={this.props.className}
      id={this.props.id}
      placeholder={this.props.placeholder}
      onBlur={this._save}
      value={this.state.value}
      />
    );
  },

  _save: function() {
    this.props.onSave(this.state.value);
    this.setState({value: ''
  });
});

Я не вижу плюсов и минусов двух альтернатив, если таковые существуют. Спасибо.

gabrielgiussi
источник
Я что-то упустил? Почему бы вам не использовать объект события для получения значений формы? Кажется, это единственная причина, по которой нужно использовать форму здесь. Если вы не используете поведение отправки по умолчанию и имеете ссылки на входные данные, вам не нужно оборачивать их в форму.
NectarSoft

Ответы:

143

Краткая версия: избегайте ссылок.


Они плохо подходят для обслуживания и во многом теряют простоту, которую обеспечивает визуализация модели WYSIWYG.

У вас есть форма. Вам нужно добавить кнопку, которая сбрасывает форму.

  • рефов:
    • манипулировать DOM
    • render описывает, как форма выглядела 3 минуты назад
  • штат
    • SetState
    • render описывает, как выглядит форма

У вас есть поле номера CCV во входных данных и некоторые другие поля в вашем приложении, которые являются числами. Теперь вам нужно заставить пользователя вводить только цифры.

  • рефов:
    • добавить обработчик onChange (разве мы не используем refs, чтобы этого избежать?)
    • манипулировать dom в onChange, если это не число
  • штат
    • у вас уже есть обработчик onChange
    • добавить оператор if, если он недействителен, ничего не делать
    • render вызывается только в том случае, если он будет давать другой результат

Эх, неважно, премьер-министр хочет, чтобы мы просто сделали красный прямоугольник, если он недействителен.

  • рефов:
    • заставить обработчик onChange просто вызвать forceUpdate или что-то в этом роде?
    • сделать вывод рендера на основе ... а?
    • где мы берем значение для проверки при рендеринге?
    • вручную управлять свойством dom className элемента?
    • я потерялся
    • переписать без рефов?
    • читать из dom в рендере, если мы смонтированы, иначе считать действительным?
  • штат:
    • удалить оператор if
    • сделать визуализацию валидной на основе this.state

Нам нужно вернуть контроль родителю. Данные теперь в реквизитах, и нам нужно реагировать на изменения.

  • рефов:
    • реализовать componentDidMount, componentWillUpdate и componentDidUpdate
    • вручную различать предыдущие реквизиты
    • манипулировать домом с минимальным набором изменений
    • Привет! мы реализуем реакцию в реакции ...
    • есть еще, но мои пальцы болят
  • штат:
    • sed -e 's/this.state/this.props/' 's/handleChange/onChange/' -i form.js

Люди думают, что refs «проще», чем держать его в состоянии. Это может быть правдой в течение первых 20 минут, но по моему опыту после этого это не так. Дайте себе возможность сказать: «Да, я сделаю это за 5 минут», а не «Конечно, я просто перепишу несколько компонентов».

разбойник
источник
3
Не могли бы вы подробнее рассказать о sed -e 's / this.state / this.props /' 's / handleChange / onChange /' -i form.js?
gabrielgiussi 08
1
Нет, я имею в виду фактические изменения в домене. React.findDOMNode(this.refs.foo). Если вы, например, измените, this.refs.foo.props.barничего не произойдет.
Brigand
1
например, если вы <input onChange={this.handleChange} value={this.state.foo} />изменили его на <input onChange={this.props.handleChange} value={this.props.foo} />или изменили функцию (-ы) handleChange для вызова обратного (-ых) обратного вызова (-ов) в props. В любом случае, это несколько небольших очевидных изменений.
Brigand
4
Не уверен, что я единственный, кого ваш ответ немного сбивает с толку. Не могли бы вы показать несколько примеров кода, чтобы пояснить вашу точку зрения?
Ришаб,
2
Наличие 50+ входов на экране, рендеринг каждого при любом изменении состояния нежелательно. Компонентировать каждое inputполе, каждое из которого поддерживает свое состояние, является идеальным. В какой-то момент нам действительно нужно согласовать эти различные независимые состояния с какой-то более крупной моделью. Может быть, у нас есть автосохранение на таймере, или мы просто экономим на componentWillUnmountэтом, я считаю refsидеальным, во время согласования мы извлекаем stateзначение из каждого ref, и никто не станет мудрее. Я согласен, что в большинстве случаев stateэто ответ, но при большом количестве inputsиспользование правильного refsшаблона является преимуществом для производительности
lux
105

Я видел, как несколько людей цитировали приведенный выше ответ как причину «никогда не использовать ссылки», и я хочу высказать свое (как и несколько других разработчиков React, с которыми я разговаривал) мнение.

Утверждение «не использовать ссылки» является правильным, когда речь идет об их использовании для экземпляров компонентов. Это означает, что вы не должны использовать ссылки как способ захвата экземпляров компонентов и вызова на них методов. Это неправильный способ использования рефери, когда рефери быстро уходят на юг.

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

Редактировать 2019: Здравствуйте, друзья будущего. В дополнение к тому, что я упоминал несколько лет назад ^, с помощью React Hooks ссылки также являются отличным способом отслеживать данные между рендерами и не ограничиваются только захватом узлов DOM.

Тайлер МакГиннис
источник
3
Ваш последний абзац имеет смысл, но можете ли вы пояснить второй абзац? Каков конкретный пример получения экземпляра компонента и вызова метода, который будет считаться неправильным?
Дэнни Либин
2
Я согласен с этим. Я использую refs до тех пор, пока мне не понадобится выполнять проверку или манипулировать значением поля. Если мне действительно нужно подтвердить изменение или изменение значений программно, я использую состояние.
Кристофер Дэвис,
1
Я тоже с этим согласен. На этапе открытия я намеренно с наивностью подошел к экрану с большим количеством входов. Все входные значения хранятся на карте (в состоянии) с ключом id. Излишне говорить, что производительность снизилась из-за того, что настройка состояния и рендеринг 50+ входов (некоторые материалы-ui, которые были тяжелыми!) При таких незначительных изменениях пользовательского интерфейса, как щелчок по флажку, не были идеальными. Компонентирование каждого входа, который может поддерживать собственное состояние, казалось правильным подходом. Если требуется согласование, просто загляните в refsи получите значение состояния. На самом деле это кажется действительно красивым узором.
люкс
2
Я полностью согласен. На мой взгляд, принятый ответ слишком расплывчатый.
Джеймс Райт
Я согласен. При разработке общего компонента формы это позволяет выявить болевые точки контролируемых компонентов и управления фокусом, обработки ошибок и т. Д. Фактически невозможно иметь чистую архитектуру. Поговори со мной, если потребуется. Я перемещаю свои компоненты в refs.
kushalvm
6

TL; DR Вообще говоря, refsидите вразрез с декларативной философией React , поэтому вы должны использовать их в крайнем случае. Используйте по state / propsвозможности.


Чтобы понять, где вы используете refsvs state / props, давайте рассмотрим некоторые принципы дизайна, которым следует React.

Документация Per React оrefs

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

Принципы проектирования аварийных люков в соответствии с принципами React

Если какой-то паттерн, полезный для создания приложений, трудно выразить декларативно, мы предоставим для него императивный API. (и они ссылаются на ссылки здесь)

Это означает, что команда React предлагает избегать refsи использовать state / propsдля всего, что можно сделать реактивным / декларативным способом.

@Tyler McGinnis дал очень хороший ответ , заявив, что

Правильный (и очень полезный) способ использования ссылок - это когда вы используете их для получения некоторого значения из DOM ...

Хотя вы можете это сделать, вы будете работать против философии React. Если у вас есть значение во входных данных, оно наверняка исходит из state / props. Чтобы код оставался последовательным и предсказуемым, вам также следует придерживаться state / propsэтого правила. Я признаю тот факт, что refsиногда это дает вам более быстрое решение, поэтому, если вы делаете доказательство концепции, приемлемо быстрое и грязное .

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

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

Любомир
источник
5

Это старый пост

Я поделюсь своим небольшим опытом по одному делу по этому поводу.

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

Сначала я работал с состоянием, чтобы обрабатывать значения входных данных:

  const [inputsValues, setInputsValues] = useState([])
  const setInputValue = (id, value) => {
    const arr = [...inputsValues]
    arr[id] = value
    setInputsValues(arr)
  }

и, конечно же, во входах:

value={inputsValues[id] || ''}
onChange={event => setInputValue(id, event.target.value)}

Рендеринг был настолько тяжелым, что изменение ввода было прерывистым, как **** (не пытайтесь удерживать клавишу нажатой, текст появится только после паузы)

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

закончилось так:

  const inputsRef = useRef([])

и во входах:

ref={input => (inputsRef.current[id] = input)}

[ну, в моем случае Input был Material-UI TextField, так что это было:

inputRef={input => (inputsRef.current[id] = input)}

]

Благодаря этому нет повторного рендеринга, ввод плавный, функциональность работает точно так же. Это сэкономит циклы и вычисления, а значит, и энергию. Сделайте это для земли x)

Мой вывод: может даже понадобиться useRef для значений input.

Капхар
источник