Tutorial React: Agregando estado
27 de junio 2015
Primeros pasos manipulando el state de los componentes
Agregando estado al formulario
Cuando se enfoque el campo de título, el formulario debe mostrarse entero. Como las diferentes partes están ocultas con CSS, pueden mostrarse simplemente añadiendo la clase open
al elemento <form>
(usando el css de ejemplo).
Entonces podemos decir que el componente Form
tiene dos estados: abierto y cerrado. Haremos uso del state
de React para cambiar este estado cuando sea necesario, y así reflejar dichos cambios de estado en el DOM.
Agregamos el método getInitialState()
al componente en Form.js
, que devolverá el estado inicial del componente. El state de un componente es siempre un objeto plano.
getInitialState: function() {
return {
open: false
};
},
También agregamos los métodos open()
y close()
que servirán para cambiar el state del componente. Obviamente, el primero abrirá el formulario, mientras que el segundo lo cerrará.
open: function() {
this.setState({
open: true
});
},
close: function() {
this.setState({
open: false
});
},
Ahora es necesario reflejar los cambios de estado en el DOM. Para ello, hacemos una comprobación de verdad de la propiedad open
del state. Accedemos a ella con this.state.open
. Si el estado es true
, agregamos la clase open
al <form>
. de lo contrario, no agregamos nada. Usaremos una expresión ternaria que se verá así: (this.state.open ? ' open' : '')
y la acoplaremos al atributo className
de la etiqueta <form>
:
<form className={"addnote" + (this.state.open ? ' open' : '')}>
Nota: Para usar expresiones javascript en JSX, debemos escribirlas entre llaves ({
y }
) tal como se ve en el atributo className
.
Ahora necesitamos llamar al método open()
cada vez que se enfoque el formulario, y llamar al método close()
cada vez que se haga click fuera del mismo.
Para escuchar un evento (en este caso, el evento focus
) agregamos el atributo onFocus
a la etiqueta <form>
que acabamos de editar, y le pasamos como valor una referencia a la función que ejecutará, que será el método open()
. Para que el valor pasado sea en efecto una referencia al método y no una cadena de texto, es necesario pasarlo como expresión (usando llaves).
<form className={"addnote" + (this.state.open ? ' open' : '')} onFocus={this.open}>
Ahora viene una parte ligeramente complicada de entender si no se está bien familiarizado con los eventos de JavaScript, así que atención.
Para que el formulario se cierre al hacer click fuera de él, es necesario escuchar al evento click
en todo el documento. Para esto, debemos usar algo como un document.addEventListener('click', this.close)
cada vez que el formulario sea abierto. Para evitar memory leaks, es necesario también remover este listener cada vez que el formulario sea cerrado, usando un document.removeEventListener('click', this.close)
. Finalmente, los clicks que se realizen dentro del formulario se propagarán por el DOM y activaran este listener, cerrando el formulario. Para evitar esto, es necesario usar event.stopPropagation()
en los clicks que se realicen dentro del <form>
.
Nota: Si no entendiste nada, googlea eventos javascript.
Entonces, debemos:
- Agregar el eventListener cada vez que el formulario se abre. Lo hacemos en el método
open()
justo antes de llamar elsetState()
. - Eliminar el eventListener cada vez que el formulario se cierra. Lo hacemos de igual manera en el método
close()
. - Evitar la propagación de clicks dentro del formulario. Para esto, agregaremos un nuevo método especial, llamado
componentDidMount()
. Este método especial es ejecutado una única vez, cuando el componente es insertado en el DOM. Aprovecharemos esto para atar un eventListener al elemento DOM que comanda nuestro componente, que llame astopPropagation()
. Para obtener una referencia a dicho elemento DOM, haremos uso deReact.findDOMNode()
, función que acepta un elemento react como argumento y devuelve su contraparte en el DOM.
Con todas las modificaciones hechas, el componente debería quedar así:
var React = require('react');
var Form = React.createClass({
getInitialState: function() {
return {
open: false
};
},
open: function() {
document.addEventListener('click', this.close);
this.setState({
open: true
});
},
close: function() {
document.removeEventListener('click', this.close);
this.setState({
open: false
});
},
render: function() {
return (
<form className={"addnote" + (this.state.open ? ' open' : '')} onFocus={this.open}>
<input className="addnote-title" type="text" placeholder="Título" />
<textarea className="addnote-text" placeholder="Añadir nota" />
<div className="addnote-toolbar">
<button>Hecho</button>
<a className="addnote-btn-list" />
</div>
</form>
);
},
componentDidMount: function() {
React.findDOMNode(this).addEventListener('click', function(e){
e.stopPropagation();
});
}
});
module.exports = Form;
Compilamos con browserify -t reactify ./react/app.js -o ./js/main.js
y abrimos la aplicación. El formulario ahora debería abrirse y cerrarse como es debido.