J'essaie de créer un formulaire simple avec react, mais j'ai du mal à faire en sorte que les données se lient correctement à la valeur par défaut du formulaire.
Le comportement que je recherche est le suivant:
Cependant, à l'heure actuelle, je constate que le champ de saisie de texte est vide chaque fois que j'actualise la page. (Bien que ce que je tape dans l'entrée enregistre correctement et persiste.) Je pense que cela est dû au fait que le code HTML du champ de saisie se charge lorsque AwayMessage est un objet vide, mais ne s'actualise pas lors du chargement de awayMessage. De plus, je ne peux pas spécifier de valeur par défaut pour le champ.
J'ai supprimé une partie du code pour plus de clarté (c'est-à-dire 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"]=@refs["master_toggle"].getDOMNode().checked
console.log "value of text", @refs["text"].getDOMNode().value
awayMessage["text"]=@refs["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>
mon console.log pour AwayMessage montre ce qui suit:
AWAY_MESSAGE Object {}
AWAY_MESSAGE Object {id: 1, company_id: 1, text: "Sample Text", master_toggle: false}
Si vous souhaitez initialiser l'entrée, vous devez utiliser defaultValue, mais si vous souhaitez utiliser state pour modifier la valeur, vous devez utiliser value. Personnellement, j'aime bien utiliser defaultValue si je l'initialise, puis utiliser les références pour obtenir la valeur lors de l'envoi. Il y a plus d'informations sur les références et les entrées sur les documents de réaction, https://facebook.github.io/react/docs/forms.html et https://facebook.github.io /react/docs/working-with-the-browser.html .
Voici comment je voudrais réécrire votre entrée:
awayMessageText = if this.state.awayMessage then this.state.awayMessage.text else ''
<input ref="text" onChange={this.onTextChange} placeholder="Placeholder Text" value={@state.awayMessageText} />
En outre, vous ne souhaitez pas transmettre de texte de substitution comme vous l'avez fait, car la valeur sera définie sur "texte de substitution". Vous devez toujours passer une valeur vide dans l'entrée, car indéfini et nil la transforment en valeur par défaut. https://facebook.github.io/react/tips/controlled-input-null-value.html .
Vous devez passer des appels api après l'exécution de getInitialState. Pour votre cas, je le ferais dans composantDidMount. Suivez cet exemple, https://facebook.github.io/react/tips/initial-ajax.html .
Je vous recommande également de lire le cycle de vie des composants avec react. https://facebook.github.io/react/docs/component-specs.html .
Personnellement, je n'aime pas faire le tout, sinon la logique dans le rendu et préfère utiliser 'loading' dans mon état et rendre une police géniale spinner avant le chargement du formulaire, http: //fortawesome.github. io/Font-Awesome/examples / . Voici une réécriture pour vous montrer ce que je veux dire. Si je me suis trompé dans cjsx, c’est parce que j’utilise normalement du café comme celui-ci,.
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>
Cela devrait à propos de le couvrir. Maintenant, c’est une façon de traiter les formes qui utilisent l’état et la valeur. Vous pouvez également simplement utiliser defaultValue au lieu de valeur, puis utiliser refs pour obtenir les valeurs lors de la soumission. Si vous choisissez cette voie, je vous recommanderais de disposer d'un composant Shell externe (généralement appelé composants d'ordre élevé) pour extraire les données, puis les transmettre à la fiche sous forme d'accessoires.
Globalement, je vous recommande de lire la documentation de réaction tout au long et de faire quelques tutoriels. Il y a beaucoup de blogs et http://www.egghead.io avait de bons tutoriels. J'ai aussi des articles sur mon site, http://www.openmindedinnovations.com .
Une autre solution consiste à modifier le key
de l'entrée.
<input ref="text" key={this.state.awayMessage ? 'notLoadedYet' : 'loaded'} onChange={this.onTextChange} defaultValue={awayMessageText} />
Mise à jour: depuis que nous obtenons des votes positifs, je dois dire que vous devez correctement avoir un accessoire disabled
ou readonly
pendant le chargement du contenu, afin de ne pas diminuer l'expérience ux.
Et oui, c'est probablement un bidouillage, mais le travail est fait .. ;-)
Peut-être pas la meilleure solution, mais je créerais un composant comme ci-dessous afin de pouvoir le réutiliser partout dans mon code. Je souhaite qu'il était déjà en réaction par défaut.
<MagicInput type="text" binding={[this, 'awayMessage.text']} />
Le composant peut ressembler à:
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()}
/>`
Où changement par tableau est une fonction accédant au hachage par un chemin exprimé par un tableau
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
La méthode la plus fiable pour définir les valeurs initiales consiste à utiliser componentDidMount () {} en plus de render () {}:
...
componentDidMount(){
const {nameFirst, nameSecond, checkedStatus} = this.props;
document.querySelector('.nameFirst').value = nameFirst;
document.querySelector('.nameSecond').value = nameSecond;
document.querySelector('.checkedStatus').checked = checkedStatus;
return;
}
...
Vous pouvez trouver facile de détruire un élément et de le remplacer par le nouveau par
<input defaultValue={this.props.name}/>
comme ça:
if(document.querySelector("#myParentElement")){
ReactDOM.unmountComponentAtNode(document.querySelector("#myParentElement"));
ReactDOM.render(
<MyComponent name={name} />,
document.querySelector("#myParentElement")
);
};
Vous pouvez également utiliser cette version de la méthode unmount:
ReactDOM.unmountComponentAtNode(ReactDOM.findDOMNode(this).parentNode);
Donnez une valeur au paramètre "placeHolder". Par exemple :-
<input
type="text"
placeHolder="Search product name."
style={{border:'1px solid #c5c5c5', padding:font*0.005,cursor:'text'}}
value={this.state.productSearchText}
onChange={this.handleChangeProductSearchText}
/>