React - зміна неконтрольованого вводу


359

У мене є простий компонент реагування з формою, яка, як я вважаю, має один керований вхід:

import React from 'react';

export default class MyForm extends React.Component {
    constructor(props) {
        super(props);
        this.state = {}
    }

    render() {
        return (
            <form className="add-support-staff-form">
                <input name="name" type="text" value={this.state.name} onChange={this.onFieldChange('name').bind(this)}/>
            </form>
        )
    }

    onFieldChange(fieldName) {
        return function (event) {
            this.setState({[fieldName]: event.target.value});
        }
    }
}

export default MyForm;

Під час запуску програми я отримую таке попередження:

Попередження: MyForm змінює неконтрольований ввід тексту типу, яким слід керувати. Елементи введення не повинні переходити від неконтрольованих до контрольованих (або навпаки). Вирішіть між використанням керованого або неконтрольованого вхідного елемента протягом життя компонента

Я вважаю, що моє введення контролюється, оскільки воно має значення. Мені цікаво, що я роблю неправильно?

Я використовую React 15.1.0

Відповіді:


509

Я вважаю, що моє введення контролюється, оскільки воно має значення.

Для керування входом його значення повинно відповідати значенню змінної стану.

Ця умова спочатку не виконується у вашому прикладі, оскільки this.state.nameвона спочатку не встановлена. Тому введення спочатку неконтрольоване. Як тільки onChangeобробник спрацьовує вперше, this.state.nameвстановлюється. У цей момент вищезазначена умова виконується і вхід вважається контрольованим. Цей перехід від неконтрольованого до керованого призводить до помилки, поміченої вище.

Ініціалізуючи this.state.nameв конструкторі:

напр

this.state = { name: '' };

вхід буде контролюватися з самого початку, виправляючи проблему. Щоб отримати додаткові приклади, див. Реагувані контрольовані компоненти .

Незалежно від цієї помилки, ви повинні мати лише один експорт за замовчуванням. Ваш код вище два.


7
Важко прочитати відповідь і слідувати ідеї багато разів, але ця відповідь є ідеальним способом розповіді історії та змусити глядача зрозуміти водночас. Відповідь рівня бога!
surajnew55

2
Що робити, якщо у вас є динамічні поля в циклі? наприклад, ви вказали ім'я поля name={'question_groups.${questionItem.id}'}?
користувач3574492,

2
Як би працювали динамічні поля. Динамічно генерую форму, а потім встановлюю імена полів у об'єкті всередині стану, чи все-таки мені потрібно спочатку вручну встановити імена полів у стані, а потім передати їх моєму об’єкту?
Йосип

13
Ця відповідь трохи невірна. Вхід контролюється, якщо valueопора має ненульове / невизначене значення. Підпору не потрібно відповідати змінній стану (це може бути просто константою, і компонент все ще вважатиметься контрольованим). Відповідь Адама правильніша і її слід прийняти.
ecraig12345

2
Так - це технічно неправильна відповідь (але корисна). Зауваження щодо цього - якщо ви маєте справу з перемикачами (значення яких контролюється трохи інакше), це попередження про реагування може насправді кинутись, навіть якщо ваш компонент контролюється (в даний час). github.com/facebook/react/isissue/6779 , і його можна виправити, додавши !! до правдивості isChecked
mheavers

123

Коли ви вперше візуалізуєте свій компонент, this.state.nameвін не встановлений, тож він оцінюється до undefinedабо null, і ви переходите до вашого value={undefined}чи value={null}вашого input.

Коли ReactDOM перевіряє, чи контролюється поле, він перевіряє, чи єvalue != null (зауважте, що це !=не так !==), а оскільки undefined == nullв JavaScript він вирішує, що це неконтрольовано.

Отже, коли onFieldChange()викликається, this.state.nameвстановлюється значення рядка, ваш вхід переходить від неконтрольованого до контролю.

Якщо ви це зробите this.state = {name: ''}в своєму конструкторі, адже '' != nullваш вхід буде мати значення цілий час, і це повідомлення піде назовні.


5
Що варто, те ж саме може статися і з тим this.props.<whatever>, що було проблемою в моєму кінці. Спасибі, Адам!
Дон

Так! Це також може статися, якщо ви передасте обчислену змінну, яку ви визначаєте render(), або вираз у самому тезі - все, що оцінює undefined. Радий, що можу допомогти!
Лей Бренецький

1
Дякую за цю відповідь: навіть якщо це не прийнято, це пояснює проблему набагато краще, ніж просто "вказати name".
machineghost

1
Я оновив свою відповідь, щоб пояснити, як працює керований ввід. Варто зазначити, що тут важливо не те, що значення undefinedпередається спочатку. Швидше, це факт, що this.state.nameне існує як змінна стан, що робить вхід неконтрольованим. Наприклад, this.state = { name: undefined };це призвело б до керування входом. Слід розуміти, що важливим є те, звідки походить цінність, а не те, що значення.
fvgs

1
@fvgs this.state = { name: undefined }все одно призведе до неконтрольованого введення. <input value={this.state.name} />десугар до React.createElement('input', {value: this.state.name}). Оскільки звернення до неіснуючого властивості об'єкта повертається undefined, воно оцінює точно такий же виклик функції React.createElement('input', {value: undefined})- nameякий не встановлений або явно встановлений undefined, тому React поводиться так само Ви можете бачити таку поведінку в цій JSFiddle.
Лей Бренецький

52

Іншим підходом може бути встановлення значень за замовчуванням всередині вашого вводу, наприклад, такий:

 <input name="name" type="text" value={this.state.name || ''} onChange={this.onFieldChange('name').bind(this)}/>

1
<Ім'я вхідного = тип "ім'я" = "текст" DefaultValue = "" OnChange = {this.onFieldChange ( 'ім'я') пов'язують (це).} /> Я думаю , що це також буде працювати
Гендальф

Велике спасибі, саме це я шукав. Чи слід мати на увазі якісь недоліки чи потенційні проблеми з використанням цієї моделі?
Марсель Оттен

Це працювало для мене, оскільки поля динамічно відображаються з API, тому я не знаю, яке ім'я поля, коли компонент встановлений. Це спрацювало частування!
Джеймі - Fenrir Digital Ltd

18

Я знаю, що інші вже відповіли на це. Але тут дуже важливий фактор, який може допомогти іншим людям, які відчувають подібні проблеми:

У onChangeполі введення даних повинен бути доданий обробник (наприклад, textField, прапорець, радіо тощо). Завжди обробляйте діяльність через onChangeобробник.

Приклад:

<input ... onChange={ this.myChangeHandler} ... />

Під час роботи з прапором вам може знадобитися обробляти її checkedстан !!.

Приклад:

<input type="checkbox" checked={!!this.state.someValue} onChange={.....} >

Довідка: https://github.com/facebook/react/isissue/6779#issuecomment-326314716


1
це працює для мене, дякую, так, я на 100% згоден з цим, початковий стан - {}, тому перевірене значення буде невизначеним і зробить його неконтрольованим,
Ping Woo

13

Просте рішення для вирішення цієї проблеми - встановити порожнє значення за замовчуванням:

<input name='myInput' value={this.state.myInput || ''} onChange={this.handleChange} />

8

Один з потенційних недоліків у встановленні значення поля "" (порожній рядок) у конструкторі - це те, що поле є необов'язковим полем і не змінюється. Якщо ви не зробите деякий масаж перед публікацією форми, поле збережеться для зберігання даних як порожній рядок замість NULL.

Ця альтернатива дозволить уникнути порожніх рядків:

constructor(props) {
    super(props);
    this.state = {
        name: null
    }
}

... 

<input name="name" type="text" value={this.state.name || ''}/>

6

Під час використання onChange={this.onFieldChange('name').bind(this)}у своєму введенні ви повинні оголосити свій стан порожнім рядком як значення властивості поля.

неправильний спосіб:

this.state ={
       fields: {},
       errors: {},
       disabled : false
    }

правильний шлях:

this.state ={
       fields: {
         name:'',
         email: '',
         message: ''
       },
       errors: {},
       disabled : false
    }

6

У моєму випадку мені не вистачало чогось справді тривіального.

<input value={state.myObject.inputValue} />

Мій стан був таким, коли я отримував попередження:

state = {
   myObject: undefined
}

Чергуючи свій стан на посилання на вхід моєї вартості , мою проблему було вирішено:

state = {
   myObject: {
      inputValue: ''
   }
}

2
Дякую, що допомогли мені зрозуміти справжню проблему, з якою я
стикався

3

Якщо реквізит вашого компонента був переданий як стан, поставте значення за замовчуванням для вхідних тегів

<input type="text" placeholder={object.property} value={object.property ? object.property : ""}>


3

Оновлення для цього. Для використання React Hooksconst [name, setName] = useState(" ")


Спасибі 4 оновлення Йорданії, цю помилку трохи важко вирішити
Хуан Сальвадор

Чому " "і ні ""? Це призводить до того, що ви втрачаєте будь-який текст підказки, і якщо ви натискаєте та набираєте, ви отримуєте підлий пробіл перед будь-якими введеними даними.
Джошуа Вейд

1

Зазвичай це відбувається лише тоді, коли ви не контролюєте значення поданої, коли програма запускалася, і після того, як якась подія або якась функція була запущена або стан змінився, ви зараз намагаєтесь керувати значенням у полі введення.

Цей перехід відсутності контролю над входом, а потім контролю над ним - це те, що спричиняє проблему в першу чергу.

Найкращий спосіб уникнути цього - оголосивши якесь значення для введення в конструктор компонента. Так що елемент введення має значення з початку програми.


0

Для динамічного налаштування властивостей стану для входів форми та контролю їх контролю ви можете зробити щось подібне:

const inputs = [
    { name: 'email', type: 'email', placeholder: "Enter your email"},
    { name: 'password', type: 'password', placeholder: "Enter your password"},
    { name: 'passwordConfirm', type: 'password', placeholder: "Confirm your password"},
]

class Form extends Component {
  constructor(props){
    super(props)
    this.state = {} // Notice no explicit state is set in the constructor
  }

  handleChange = (e) => {
    const { name, value } = e.target;

    this.setState({
      [name]: value
    }
  }

  handleSubmit = (e) => {
    // do something
  }

  render() {
     <form onSubmit={(e) => handleSubmit(e)}>
       { inputs.length ?
         inputs.map(input => {
           const { name, placeholder, type } = input;
           const value = this.state[name] || ''; // Does it exist? If so use it, if not use an empty string

           return <input key={name}  type={type} name={name} placeholder={placeholder} value={value} onChange={this.handleChange}/>
       }) :
         null
       }
       <button type="submit" onClick={(e) => e.preventDefault }>Submit</button>
     </form>    
  }
}

0

Просто створіть резервну копію до '', якщо this.state.name є нульовим.

<input name="name" type="text" value={this.state.name || ''} onChange={this.onFieldChange('name').bind(this)}/>

Це також працює зі змінними useState.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.