Подтвердить что ты не робот

Передовая практика для компонентов формы ReactJS

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

React.createClass({
    getInitialState: function() {
        return {
            entity: {
                property1: null,
                property2: null
            }
        };
    },

    handleChange: function(e) {
        var entity = this.state.entity;

        switch(e.target.name) {
            case 'property1':
                entity.property1 = e.target.value;
                break;
            case 'property2':
                entity.property2 = e.target.value;
                break;
        }

        this.setState({
            entity: entity
        });
    },

    render: function() {
        return (
            <div className="entity-form">
                <form onChange={this.handleChange}>
                    <input type="text" name="property1" value={this.state.entity.property1} />
                    <br />

                    <textarea name="property2" value={this.state.entity.property2}></textarea>
                    <br />
                </form>
            </div>
        );
    }
});

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

В теории, я мог бы, чтобы весь объект состояния представлял объект, который редактируется, поэтому каждое свойство объекта является переменными состояния первого уровня. Тем не менее, я хочу иметь возможность добавлять дополнительные переменные состояния для функций GUI и другие вещи, связанные с тем, что будет делать компонент, поэтому я предпочел бы, чтобы объект объекта был одной переменной состояния, такой как переменная состояния "entity" выше. Конечно, объект может быть более сложным объектом, например, моделью Backbone или аналогичной, но в этом упрощенном примере я просто использую простой объект с требуемыми свойствами.

Итак, в поисках наилучшего способа сделать компоненты React для этой цели, у меня есть несколько вопросов:

  • Реквизит или состояние.

В этом случае я решил поместить объект entity с содержимым для формы в переменную состояния вместо prop. Это позволяет обновлять объект во время ввода формы без вызова родителя и обновления реквизита. Что касается моего опыта React, это будет лучшей практикой для компонента формы, подобного этому.

  1. Управляемые или неконтролируемые входы.

В упрощенном примере выше я использую управляемые входы. Это приводит к обновлению состояния и повторному рендерингу компонента при каждом изменении (как и каждый символ, введенный в текстовое поле). Это лучшая практика? Хорошо, что компонент имеет полный контроль над тем, что происходит, вместо параметров defaultValue и на каком-либо событии (например, нажатии кнопки сохранения пользователем) компонент извлекает значения, обновляет объект и сохраняет его на сервере. Существуют ли какие-либо причины (или мнения), если в таких случаях следует использовать контролируемые или неконтролируемые входы?

  1. onChange для формы или каждого входа

В примере есть onChange в теге формы, и он вызывает метод handleChange каждый раз, когда изменяется любое из полей в форме. Однако, поскольку входы управляются (имеют значения параметров), React жалуется, что поля ввода не имеют свойства onChange. Означает ли это, что общий критерий onChange в теге формы является плохой практикой, и я должен удалить его и вместо этого поставить onChange на каждое отдельное поле?

  1. Обновление отдельных свойств

В приведенном выше примере я использую коммутатор на основе того, какое поле ввода обновляется (когда вызывается handleChange). Наверное, я мог бы вместо этого убедиться, что все имена полей синхронизированы с именами свойств объекта, и я могу установить свойства объекта entity в handleChange на основе имени поля из события (e.target.name). Однако это затрудняет индивидуальные потребности в поле, даже если большинство полей просто обновляют свойство объекта напрямую. Я предполагаю, что alternativ - это переключатель с настройкой по умолчанию на основе имени ввода и блоки case для любого поля, для которого требуются другие способы обновления (например, фильтрация значения перед установкой его на объект). Пожалуйста, прокомментируйте это, если вы знаете, что гораздо лучший способ обработки полевых обновлений таким образом.

  1. Обновление объекта состояния

Одной большой проблемой этого примера является способ обновления объекта сущности. Поскольку переменная entity в handleChange задана объектом сущности из текущего состояния, это всего лишь указатель, и обновление переменной сущности изменяет объект в состоянии. На страницах React говорится, что вы никогда не должны обновлять состояние напрямую. Одна из причин - это то, что я испытал при обновлении состояния до вызова setState. Если у вас есть метод mustComponentUpdate, prevState содержит новое состояние, поскольку содержимое аргумента prevState, отправленного в файл shouldComponentUpdate, основывается на том, что было в состоянии при вызове setState. Насколько я знаю, нет простого способа клонировать объект в javascript. Поэтому возникает вопрос, когда у меня есть целые объекты, которые мне нужно обновлять свойства (и не касаться других значений в объекте), а не просто запускать setState одной переменной состояния, что лучший способ сделать это, не вызывая эти виды смешанных состояний?

4b9b3361

Ответ 1

  • Все, что собирается изменить, находится в состоянии.
  • Если вы ищете загрузку существующего объекта и его редактирование, вам нужны управляемые входы, и вы хотите соответствующим образом установить значения. В большинстве случаев я стараюсь держаться подальше от defaultValue (вне выпадающих списков).
  • Это связано с вашим предыдущим вопросом. Если вы укажете значение, вы используете управляемый вход, и вам необходимо предоставить обработчик onChange для любого управляемого входа, иначе он будет установлен на камне. Преимущество контролируемых входных данных заключается в том, что пользователи не могут редактировать их, поэтому, если у вас были заблокированы некоторые свойства (возможно, только для чтения, соображения безопасности), когда пользователь пытается сохранить, даже если они отредактировали HTML напрямую, React должен потянуть значение свойства из представления vDOM (может быть, неправильно здесь, но я считаю, что я тестировал это раньше). В любом случае, вы должны иметь onChange, установленный непосредственно на управляемых входах. Что касается использования дегазации событий (на уровне формы), это не является плохой практикой для многих событий, это всего лишь конкретный сценарий (управляемые входы), где вам нужны событияChange, определенные для каждого элемента.
  • Не совсем уверен, что спросит об этом, но я использовал refs вместо target.name.
  • Итак, вы правы в том, что вы никогда не должны изменять состояние напрямую, и это сложный бит из документов. React собирается изменить состояние напрямую, он просто сделает это в реализации через setState. Если вы измените состояние вне этого вызова метода, произойдет непредвиденное событие и будут сброшены ошибки.

shouldComponentUpdate делает только мелкие сравнения, но здесь есть несколько решений.

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

Лучшее решение, и я использовал с React + Flux, чтобы реализовать propertyChanged bool и просто проверить, что в вашем shouldComponentUpdate.

Теперь это потребует, чтобы вы знали, что нужно установить его, когда что-то изменится, т.е. вы изменили что-то более глубокое на графе объектов. Say propertyOne - это объект с свойством, которое изменяется в вашем методе handleChange. Вы бы подтвердили ввод, как хотите, а затем установите свойствоChanged = true, , и тогда вам необходимо реализовать componentDidUpdate. Мы делаем здесь предположение, но если компонент обновлен, вы возвращаете значение propertyChanged в значение false, чтобы у вас не было никакого последующего запуска нежелательных обновлений. Я надеюсь, что в этом есть смысл. Это похоже на односторонний notifyPropertyChanged.

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

http://jsfiddle.net/rpv9trhh/

var e = {
    prop1: 'test',
    prop2: 'wee',
    prop3: 'another property',
    propFour: 'Oh yeah!'
};

var FormComp = React.createClass({
    getInitialState: function(){
        return {
            entity: this.props.entity
        }
    },
    render: function() {

        var ent = this.state.entity;
        var that = this;
        var inputs = [];

        for(var key in ent){
            inputs.push(<input 
               key={key} 
               style={{display:'block'}} 
               type="text" 
               ref={key} onChange={that._propertyChanged.bind(null, key)}
               value={ent[key]} />)
        }

        return <form>
            {inputs}
            <input 
                type="button" 
                onClick={this._saveChanges} 
                value="Save Changes" />
        </form>;
    },
    _propertyChanged: function(propName) {
        var nextProp = this.refs[propName].getDOMNode().value;
        var nextEntity = this.state.entity;
        nextEntity[propName] = nextProp;

        this.setState({
            entity: nextEntity
        });
    },
    _saveChanges: function() {
        var updatedEntity = this.state.entity;

        for(var key in updatedEntity){
            alert(updatedEntity[key]);
        }

        //TODO: Call to service to save the entity, i.e.    
        ActionCreators.saveEntity(updatedEntity);
    }
});

React.renderComponent(<FormComp entity={e} />, document.body);