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

React input defaultValue не обновляется с состоянием

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

Поведение, которое я ищу, следующее:

  • Когда я открываю свою страницу, поле ввода текста должно быть заполнено текстом моего AwayMessage в моей базе данных. Это "Пример текста"
  • В идеале я хочу иметь местозаполнитель в поле ввода текста, если AwayMessage в моей базе данных не имеет текста.

Однако, прямо сейчас, я обнаружил, что поле ввода текста пустое при каждом обновлении страницы. (Хотя то, что я набираю на ввод, сохраняет корректно и сохраняется.) Я думаю, это связано с тем, что поле ввода html загружается, когда AwayMessage является пустым объектом, но не обновляется, когда загружается файл awayMessage. Кроме того, я не могу указать значение по умолчанию для поля.

Я удалил часть кода для ясности (т.е. onToggleChange)

    window.Pages ||= {}

    Pages.AwayMessages = React.createClass

      getInitialState: ->
        App.API.fetchAwayMessage (data) =>
        @setState awayMessage:data.away_message
        {awayMessage: {}}

      onTextChange: (event) ->
        console.log "VALUE", event.target.value

      onSubmit: (e) ->
        window.a = @
        e.preventDefault()
        awayMessage = {}
        awayMessage["master_toggle"][email protected]["master_toggle"].getDOMNode().checked
        console.log "value of text", @refs["text"].getDOMNode().value
        awayMessage["text"][email protected]["text"].getDOMNode().value
        @awayMessage(awayMessage)

      awayMessage: (awayMessage)->
        console.log "I'm saving", awayMessage
        App.API.saveAwayMessage awayMessage, (data) =>
          if data.status == 'ok'
            App.modal.closeModal()
            notificationActions.notify("Away Message saved.")
            @setState awayMessage:awayMessage

      render: ->
        console.log "AWAY_MESSAGE", this.state.awayMessage
        awayMessageText = if this.state.awayMessage then this.state.awayMessage.text else "Placeholder Text"
        `<div className="away-messages">
           <div className="header">
             <h4>Away Messages</h4>
           </div>
           <div className="content">
             <div className="input-group">
               <label for="master_toggle">On?</label>
               <input ref="master_toggle" type="checkbox" onChange={this.onToggleChange} defaultChecked={this.state.awayMessage.master_toggle} />
             </div>
             <div className="input-group">
               <label for="text">Text</label>
               <input ref="text" onChange={this.onTextChange} defaultValue={awayMessageText} />
             </div>
           </div>
           <div className="footer">
             <button className="button2" onClick={this.close}>Close</button>
             <button className="button1" onClick={this.onSubmit}>Save</button>
           </div>
         </div>

my console.log для AwayMessage показывает следующее:

AWAY_MESSAGE Object {}
AWAY_MESSAGE Object {id: 1, company_id: 1, text: "Sample Text", master_toggle: false}
4b9b3361

Ответ 1

defaultValue предназначен только для начальной загрузки

Если вы хотите инициализировать ввод, вы должны использовать defaultValue, но если вы хотите использовать состояние для изменения значения, вам нужно использовать значение. Лично мне нравится просто использовать defaultValue, если я просто инициализирую его, а затем просто использую refs, чтобы получить значение при отправке. Там больше информации о ссылках и вкладках в реакциях docs, https://facebook.github.io/react/docs/forms.html и https://facebook.github.io/react/docs/working-with-the-browser.html.

Вот как я переписал бы ваш ввод:

awayMessageText = if this.state.awayMessage then this.state.awayMessage.text else ''
<input ref="text" onChange={this.onTextChange} placeholder="Placeholder Text" value={@state.awayMessageText} />

Кроме того, вы не хотите передавать текст заполнителя, как и вы, потому что это фактически установит значение "текст заполнителя". Вам все равно нужно передать пустое значение во входные данные, потому что undefined и nil превращают значение в defaultValue по существу. https://facebook.github.io/react/tips/controlled-input-null-value.html.

getInitialState не может совершать вызовы api

Вам нужно сделать api-вызовы после запуска getInitialState. Для вашего дела я бы сделал это в компонентеDidMount. Следуйте этому примеру, https://facebook.github.io/react/tips/initial-ajax.html.

Я бы также рекомендовал прочесть на жизненном цикле компонентов с реакцией. https://facebook.github.io/react/docs/component-specs.html.

Переписать с изменениями и условиями загрузки

Лично мне не нравится делать целое if else, а затем логику в рендеринге и предпочитаю использовать "загрузку" в моем состоянии и визуализировать шрифт awesome spinner перед загрузкой формы, http://fortawesome.github.io/Font-Awesome/examples/. Здесь перепишите, чтобы показать вам, что я имею в виду. Если я испортил тики для cjsx, это потому, что я обычно просто использую coffeescript, как это,

window.Pages ||= {}

Pages.AwayMessages = React.createClass

  getInitialState: ->
    { loading: true, awayMessage: {} }

  componentDidMount: ->
    App.API.fetchAwayMessage (data) =>
      @setState awayMessage:data.away_message, loading: false

  onToggleCheckbox: (event)->
    @state.awayMessage.master_toggle = event.target.checked
    @setState(awayMessage: @state.awayMessage)

  onTextChange: (event) ->
    @state.awayMessage.text = event.target.value
    @setState(awayMessage: @state.awayMessage)

  onSubmit: (e) ->
    # Not sure what this is for. I'd be careful using globals like this
    window.a = @
    @submitAwayMessage(@state.awayMessage)

  submitAwayMessage: (awayMessage)->
    console.log "I'm saving", awayMessage
    App.API.saveAwayMessage awayMessage, (data) =>
      if data.status == 'ok'
        App.modal.closeModal()
        notificationActions.notify("Away Message saved.")
        @setState awayMessage:awayMessage

  render: ->
    if this.state.loading
      `<i className="fa fa-spinner fa-spin"></i>`
    else
    `<div className="away-messages">
       <div className="header">
         <h4>Away Messages</h4>
       </div>
       <div className="content">
         <div className="input-group">
           <label for="master_toggle">On?</label>
           <input type="checkbox" onChange={this.onToggleCheckbox} checked={this.state.awayMessage.master_toggle} />
         </div>
         <div className="input-group">
           <label for="text">Text</label>
           <input ref="text" onChange={this.onTextChange} value={this.state.awayMessage.text} />
         </div>
       </div>
       <div className="footer">
         <button className="button2" onClick={this.close}>Close</button>
         <button className="button1" onClick={this.onSubmit}>Save</button>
       </div>
     </div>

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

В целом я бы рекомендовал прочесть все интерактивные документы и сделать несколько уроков. Там было много блогов и http://www.egghead.io были хорошие учебные пособия. У меня есть кое-что на моем сайте, http://www.openmindedinnovations.com.

Ответ 2

Другим способом устранения этого является изменение ввода key.

<input ref="text" key={this.state.awayMessage ? 'notLoadedYet' : 'loaded'} onChange={this.onTextChange} defaultValue={awayMessageText} />

Обновление: Так как это получает upvotes, я должен буду сказать, что вы должны иметь поддержку disabled или readonly во время загрузки содержимого, поэтому вы не уменьшаете опыт ux.

И да, это, скорее всего, взлом, но он выполняет свою работу..; -)

Ответ 3

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

<MagicInput type="text" binding={[this, 'awayMessage.text']} />

Компонент может выглядеть так:

window.MagicInput = React.createClass

  onChange: (e) ->
    state = @props.binding[0].state
    changeByArray state, @path(), e.target.value
    @props.binding[0].setState state

  path: ->
    @props.binding[1].split('.')
  getValue: ->
    value = @props.binding[0].state
    path = @path()
    i = 0
    while i < path.length
      value = value[path[i]]
      i++
    value

  render: ->
    type = if @props.type then @props.type else 'input'
    parent_state = @props.binding[0]
    `<input
      type={type}
      onChange={this.onChange}
      value={this.getValue()}
    />`

Где изменение по массиву - это функция, обращающаяся к хешу по пути, выраженному массивом

changeByArray = (hash, array, newValue, idx) ->
  idx = if _.isUndefined(idx) then 0 else idx
  if idx == array.length - 1
    hash[array[idx]] = newValue
  else
    changeByArray hash[array[idx]], array, newValue, ++idx 

Ответ 4

хотя НЕ РЕКОМЕНДУЕТСЯ РЕАКТОМ, он работает:

render() {
    const { value } = this.state;
    return <input
        defaultValue={value}
        value={value} />;
}

Ответ 5

Вам может быть легко уничтожить элемент и заменить его на новый с помощью

<input defaultValue={this.props.name}/>

вот так:

if(document.querySelector("#myParentElement")){
    ReactDOM.unmountComponentAtNode(document.querySelector("#myParentElement"));
    ReactDOM.render(
        <MyComponent name={name}  />,
        document.querySelector("#myParentElement")
    );
};

Вы также можете использовать эту версию метода unmount:

ReactDOM.unmountComponentAtNode(ReactDOM.findDOMNode(this).parentNode);