React Fundamentals
> Sesión 03: Estado (state) y Propiedades (props)
> Ejemplo 2
- Modificar el estado.
- Modificar estado del padre por medio de funciones mandadas como props.
- Introducción de los ciclos de vida: DidMount, WillUnmount, DidUpdate.
- Entender en que momento se ejecuta cada uno.
- Usar los eventos onClick y onChange.
- Tener Node instalado.
-
Comenzar nuevo proyecto de React con el comando
npx create-react-app ejemplo2
. -
Seguir las buenas prácticas para empezar un proyecto.
-
Convertimos nuestra
App.js
en un componente stateful (clase) para usar el estado.
import React from 'react';
class App extends React.Component {
render() {
return (
<div>
Hola Mundo!
</div>
);
}
}
export default App;
- Vamos a darle un margen a la aplicación para que no se vea en la mera esquina, creamos una clase CSS y se la ponemos a nuestro
div
.
.margen {
margin: 100px;
}
- Creamos los estados necesarios para este ejercicio.
import React from 'react';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
nombre: '',
mensaje: '',
listaNombres: ['Bedu']
};
}
render() {
return (
<div className="margen">
Hola Mundo!
</div>
);
}
}
export default App;
- Creamos un
<input />
y nos aseguramos que podamos escribir en él.
import React from 'react';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
nombre: '',
mensaje: '',
listaNombres: ['Bedu']
};
}
render() {
return (
<div className="margen">
<input />
</div>
);
}
}
export default App;
-
Este va a ser el lugar en donde escribiremos el nombre para agregarlo a la lista de nombre. Para poder hacer eso necesitamos tener registro de lo que los usuarios estan escribiendo en el campo de texto.
-
Vamos a registrarlo con el estado de
nombre
.
<input value={this.state.nombre} />
- Ahora nuestro
<input />
ya no funciona, esto es porque el valor siempre es un string vacio; ahora tenemos que cambiar el estado cada que el usuario escriba algo.
import React from 'react';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
nombre: '',
mensaje: '',
listaNombres: ['Bedu']
};
};
handleInputChange = (event) => {
this.setState({
nombre: event.target.value
});
};
render() {
return (
<div className="margen">
<input
value={this.state.nombre}
onChange={this.handleInputChange}
/>
</div>
);
}
}
export default App;
-
Nuestro campo de texto vuelve a funcionar.
-
Ahora vamos a desplegar los nombres que tenemos en nuestra lista usando la función de javascript
.map()
.
render() {
return (
<div className="margen">
<input
value={this.state.nombre}
onChange={this.handleInputChange}
/>
<ul>
{this.state.listaNombres.map((nmbr) => (
<li>
{nmbr}
</li>
))}
</ul>
</div>
);
}
- Si abrimos la consola del navegador, nos va a dar un mensaje diciendo
Each child in a list should have a unique "key" prop
. Para arreglarlo hacemos lo siguiente:
{this.state.listaNombres.map((nmbr, key) => (
<li key={key}>
{nmbr}
</li>
))}
- Ya que estamos imprimiendo los nombres, vamos a agregar más nombres con un botón.
import React from 'react';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
nombre: '',
mensaje: '',
listaNombres: ['Bedu']
};
};
handleInputChange = (event) => {
this.setState({
nombre: event.target.value
});
};
handleClick = () => {
const nombreEnEstado = this.state.nombre;
if (!nombreEnEstado) return;
const listaActualizada = [
...this.state.listaNombres,
nombreEnEstado
];
this.setState({
nombre: '',
listaNombres: listaActualizada,
});
};
render() {
return (
<div className="margen">
<input
value={this.state.nombre}
onChange={this.handleInputChange}
/>
<button onClick={this.handleClick}>
Agregar
</button>
<ul>
{this.state.listaNombres.map((nmbr, key) => (
<li key={key}>
{nmbr}
</li>
))}
</ul>
</div>
);
}
}
export default App;
- Nuestra función
handleClick()
se divide en 3 partes:
- Validar si el nombre existe (que no este vacio).
- Añadir el nombre a la lista de nombres.
- Actualizar el estado (añadir nombre a la lista y limpiar el nombre).
-
Ahora podemos agregar nombres escribiendo en el campo de texto y picandole al botón.
-
Vamos a crear un nuevo componente llamado
Nombre.js
que reciba el parámetro denombre
; no olvides seguir las buenas prácticas para las propiedades (props).
import React from 'react';
import PropTypes from 'prop-types';
const Nombre = (props) => {
return (
<div>
{props.nombre}
</div>
);
};
Nombre.propTypes = {
nombre: PropTypes.string.isRequired
}
export default Nombre;
- Lo importamos y lo usamos en
App.js
.
import Nombre from './Nombre';
...
{this.state.listaNombres.map((nmbr, key) => (
<li key={key}>
<Nombre nombre={nmbr} />
</li>
))}
...
- Como vamos a usar el ciclo de vida en
Nombre.js
, necesitamos convertirlo en componente stateful (clase).
import React from 'react';
import PropTypes from 'prop-types';
class Nombre extends React.Component {
render() {
return (
<div>
{this.props.nombre}
</div>
);
}
};
Nombre.propTypes = {
nombre: PropTypes.string.isRequired
}
export default Nombre;
- Vamos a agregarle el ciclo de vida más común,
componentDidMount
(cuando se inicializa).
import React from 'react';
import PropTypes from 'prop-types';
class Nombre extends React.Component {
componentDidMount() {
alert('Te damos la bienvenida ' + this.props.nombre);
}
render() {
return (
<div>
{this.props.nombre}
</div>
);
}
};
Nombre.propTypes = {
nombre: PropTypes.string.isRequired
}
export default Nombre;
-
Si nos fijamos bien, cada que un nombre es agregado a la lista, una alerta nos aparece dándole la bienvenida a la lista. Lo que
componentDidMount
hace, es que ejecuta esa función cuando el componente se montó en el virtual DOM y esta listo para ser desplegado en pantalla. Se ejecuta una vez el componente este correcto y listo para verse. -
Para poder ver el siguiente ciclo de vida, agreguemos un botón para eliminar nombres.
render() {
return (
<div>
{this.props.nombre}
<button>
X
</button>
</div>
);
}
- Hacemos la lógica para eliminar ese nombre de la lista y se lo pasamos a
Nombre.js
. Es MUUUUUUY importante NO modificar la lista directamente, esto causa mutación de datos y es algo que siempre se debe de evitar.
...
borrarNombreDeLista = (key) => {
const copiaDeLista = [...this.state.listaNombres];
copiaDeLista.splice(key, 1);
this.setState({
listaNombres: copiaDeLista
});
};
...
{this.state.listaNombres.map((nmbr, key) => (
<li key={key}>
<Nombre
nombre={nmbr}
borrarNombreDeLista={() => this.borrarNombreDeLista(key)}
/>
</li>
))}
...
- Ahora podemos agregar el siguiente ciclo de vida
componentWillUnmount
(cuando el componente se remueve).
import React from 'react';
import PropTypes from 'prop-types';
class Nombre extends React.Component {
componentDidMount() {
alert('Te damos la bienvenida ' + this.props.nombre);
}
componentWillUnmount() {
alert('Adiós');
}
render() {
return (
<div>
{this.props.nombre}
<button onClick={this.props.borrarNombreDeLista}>
X
</button>
</div>
);
}
};
Nombre.propTypes = {
nombre: PropTypes.string.isRequired,
borrarNombreDeLista: PropTypes.func.isRequired
}
export default Nombre;
-
Si nos fijamos bien, cada que un nombre es eliminado de la lista, una alerta nos despide. Lo que
componentWillUnmount
hace, es que ejecuta esa función cuando el componente se removió en el virtual DOM y esta listo para ser eliminado de la pantalla. -
El siguiente ciclo de vida se ejecuta cada que algún estado cambia, el
componentDidUpdate
. Se lo agregamos aApp.js
debajo del constructor.
...
constructor(props) {
super(props);
this.state = {
nombre: '',
mensaje: '',
listaNombres: ['Bedu']
};
};
componentDidUpdate(prevProps, prevState) {
if (this.state.listaNombres !== prevState.listaNombres) {
this.setState({
mensaje: `Longitud de la lista es: ${this.state.listaNombres.length}`
})
}
};
...
- Agregamos el mensaje arriba del campo de texto y terminamos.
...
render() {
return (
<div className="margen">
{this.state.mensaje}
<br />
<input
value={this.state.nombre}
onChange={this.handleInputChange}
/>
<button onClick={this.handleClick}>
Agregar
</button>
<ul>
{this.state.listaNombres.map((nmbr, key) => (
<li key={key}>
<Nombre
nombre={nmbr}
borrarNombreDeLista={() => this.borrarNombreDeLista(key)}
/>
</li>
))}
</ul>
</div>
);
}
...
- Resultado: