ezakto code

Los contenidos de este blog están desactualizados.

Pero estoy pensando en actualizarlos y escribir más.

Si te interesa, dejame tu email (no spam, no newsletters). Si hay suficiente interés me pondré manos a la obra y te lo haré saber!


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:

  1. Agregar el eventListener cada vez que el formulario se abre. Lo hacemos en el método open() justo antes de llamar el setState().
  2. Eliminar el eventListener cada vez que el formulario se cierra. Lo hacemos de igual manera en el método close().
  3. 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 a stopPropagation(). Para obtener una referencia a dicho elemento DOM, haremos uso de React.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.