javascript - practicas - Reaccionar entrada defaultValue no se actualiza con el estado
react js ejemplos (5)
Estoy tratando de crear un formulario simple con react, pero tengo dificultades para que los datos se vinculen correctamente con el Valor predeterminado del formulario.
El comportamiento que estoy buscando es este:
- Cuando abro mi página, el campo de entrada de texto debe completarse con el texto de mi AwayMessage en mi base de datos. Eso es "Texto de muestra"
- Idealmente, quiero tener un marcador de posición en el campo de entrada de texto si AwayMessage en mi base de datos no tiene texto.
Sin embargo, en este momento, encuentro que el campo de entrada de texto está en blanco cada vez que actualizo la página. (Aunque lo que escribo en la entrada se guarda correctamente y persiste). Creo que esto se debe a que el html del campo de texto de entrada se carga cuando AwayMessage es un objeto vacío, pero no se actualiza cuando se carga awayMessage. Además, no puedo especificar un valor predeterminado para el campo.
Eliminé parte del código para mayor claridad (es decir, 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>
mi console.log para AwayMessage muestra lo siguiente:
AWAY_MESSAGE Object {}
AWAY_MESSAGE Object {id: 1, company_id: 1, text: "Sample Text", master_toggle: false}
defaultValue es solo para la carga inicial
Si desea inicializar la entrada, debe usar defaultValue, pero si desea usar el estado para cambiar el valor, debe usar el valor. Personalmente, me gusta usar defaultValue si solo lo estoy inicializando y luego solo usar referencias para obtener el valor cuando lo envíe. Hay más información sobre referencias y entradas en los documentos de reacción, https://facebook.github.io/react/docs/forms.html y https://facebook.github.io/react/docs/working-with-the-browser.html .
Así es como reescribiría su entrada:
awayMessageText = if this.state.awayMessage then this.state.awayMessage.text else ''''
<input ref="text" onChange={this.onTextChange} placeholder="Placeholder Text" value={@state.awayMessageText} />
Además, no desea pasar el texto del marcador de posición como lo hizo porque eso realmente establecerá el valor en ''texto de marcador de posición''. Todavía necesita pasar un valor en blanco en la entrada porque undefined y nil convierte el valor en defaultValue esencialmente. https://facebook.github.io/react/tips/controlled-input-null-value.html .
getInitialState no puede hacer llamadas api
Debe realizar llamadas api después de ejecutar getInitialState. Para su caso, lo haría en componentDidMount. Siga este ejemplo, https://facebook.github.io/react/tips/initial-ajax.html .
También recomendaría leer sobre el ciclo de vida del componente con react. https://facebook.github.io/react/docs/component-specs.html .
Reescribir con modificaciones y estado de carga
Personalmente, no me gusta hacer el todo si no es lógica en el renderizado y prefiero usar ''cargar'' en mi estado y renderizar un spinner de fuente impresionante antes de que se cargue el formulario, http://fortawesome.github.io/Font-Awesome/examples/ . Aquí hay una reescritura para mostrarle lo que quiero decir. Si estropeé los ticks para cjsx, es porque normalmente solo uso coffeescript como este,.
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>
Eso debería cubrirlo. Ahora, esa es una forma de avanzar en los formularios que usan estado y valor. También puede usar defaultValue en lugar de value y luego usar referencias para obtener los valores cuando envíe. Si sigue esa ruta, le recomendaría que tenga un componente de capa externa (generalmente denominado componentes de alto orden) para obtener los datos y luego pasarlos al formulario como accesorios.
En general, recomendaría leer los documentos de reacción hasta el final y hacer algunos tutoriales. Hay muchos blogs y http://www.egghead.io tenía algunos buenos tutoriales. También tengo algunas cosas en mi sitio, http://www.openmindedinnovations.com .
Dar valor al parámetro "placeHolder". Por ejemplo :-
<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}
/>
La forma más confiable de establecer valores iniciales es usar componentDidMount () {} además 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;
}
...
Puede resultarle fácil destruir un elemento y reemplazarlo por uno nuevo con
<input defaultValue={this.props.name}/>
Me gusta esto:
if(document.querySelector("#myParentElement")){
ReactDOM.unmountComponentAtNode(document.querySelector("#myParentElement"));
ReactDOM.render(
<MyComponent name={name} />,
document.querySelector("#myParentElement")
);
};
También puede usar esta versión del método de desmontaje:
ReactDOM.unmountComponentAtNode(ReactDOM.findDOMNode(this).parentNode);
Otra forma de solucionar esto es cambiando la
key
de la entrada.
<input ref="text" key={this.state.awayMessage ? ''notLoadedYet'' : ''loaded''} onChange={this.onTextChange} defaultValue={awayMessageText} />
Actualización: dado que esto recibe votos positivos, tendré que decir que debe tener un accesorio
disabled
o de
readonly
mientras se carga el contenido, para que no disminuya la experiencia de ux.
Y sí, es muy probable que sea un truco, pero hace el trabajo ... ;-)
Quizás no sea la mejor solución, pero crearía un componente como el siguiente para poder reutilizarlo en cualquier parte de mi código. Desearía que ya estuviera en reaccionar por defecto.
<MagicInput type="text" binding={[this, ''awayMessage.text'']} />
El componente puede verse así:
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()}
/>`
Donde change by array es una función que accede al hash mediante una ruta expresada por un array
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