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}
Ответы
Ответ 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);