Aquí introduciremos el concepto de estado
en un componente de React.
const Counter = props => {
let count = 0;
let increment = () => {
count += 1;
};
return (
<>
<div>{count}</div>
<button onClick={increment}>Increment</button>
</>
);
};
Para actualizar la interfaz la única manera que hay es a través del state
. Para poder utilizar el estado de un componente tenemos dos opciones dependiendo de si hablamos de componentes funcionales o componentes de clase:
- Componentes funcionales:
- Importar
useState
como un naming import, un hook que viene de React, directamente desde la librería. useState
devuelve un array donde el primer elemento es el valor "mutable". El segundo elemento del array es el método que actualiza el primero.- Con todo, así quedaría el componente anterior actualizado:
import React, { useState } from "react";
const Counter = props => {
const [count, setCount] = useState(0);
let increment = () => {
setCount(count + 1);
};
return (
<>
<div>{count}</div>
<button onClick={increment}>Increment</button>
</>
);
};
- Componentes de clase:
Para pasar cualquier componente funcional a uno de clase solo hay que seguir los siguientes pasos:
- Crear una clase ES6 con el mismo nombre que herede de
React.Component
. - Agregar un único método vacío llamado
render()
. - Mover el cuerpo de la función al método
render()
. - Reemplazar props con
this.props
en el cuerpo derender()
. - Borrar el resto de la declaración de la función ya vacía.
Quedaría de la siguiente manera:
class Counter extends React.Component {
render() {
return (
<>
<div>{count}</div>
<button onClick={increment}>Increment</button>
</>
);
}
}
Una vez que tenemos una clase podemos usar el state
. Para agregar estado a una clase basta con añadirlo como propiedad:
class Counter extends React.Component {
state = {
count: 0
};
render() {
return (
<>
<div>{count}</div>
<button onClick={increment}>Increment</button>
</>
);
}
}
Por lo que en vez de usar count
usaremos this.state.count
:
class Counter extends React.Component {
state = {
count: 0
};
render() {
return (
<>
<div>{this.state.count}</div>
<button onClick={increment}>Increment</button>
</>
);
}
}
El estado NUNCA se debe modificar usando this.state.count = 2
. Existe una función llamada this.setState
que pasandole un objeto con lo que queremos modificar nos actualizará el estado y se encargará de actualizar la interfaz.
Por lo que quedaría asi:
class Counter extends React.Component {
state = {
count: 0
};
increment() {
this.setState({ count: this.state.count + 1 });
}
render() {
return (
<>
<div>{this.state.count}</div>
<button onClick={() => this.increment()}>Increment</button>
</>
);
}
}
React puede agrupar varias invocaciones a setState() en una sola actualización para mejorar el rendimiento.
Debido a que this.props
y this.state
pueden actualizarse de forma asincrónica, no debes confiar en sus valores para calcular el siguiente estado.
Por ejemplo, el código anterior puede fallar en actualizar el contador. Para arreglarlo, usa una segunda forma de setState()
que acepta una función en lugar de un objeto. Esa función recibirá el estado previo como primer argumento, y las props en el momento en que se aplica la actualización como segundo argumento:
this.setState(state => ({
count: state.count + 1
}));
Cuando invocas a setState(), React combina el objeto que proporcionaste con el estado actual.
Por ejemplo, tu estado puede contener varias variables independientes:
state = {
posts: [],
comments: []
};
Luego puedes actualizarlas independientemente con invocaciones separadas a setState():
fetchPosts().then(response => {
this.setState({
posts: response.posts
});
});
fetchComments().then(response => {
this.setState({
comments: response.comments
});
});
Ni los componentes padres o hijos pueden saber si un determinado componente tiene o no tiene estado y no les debería importar si se define como una función o una clase.
Por eso, al estado a menudo se le denomina local o encapsulado. No es accesible desde otro componente excepto de aquel que lo posee y lo asigna.
Un componente puede elegir pasar su estado como props
a sus componentes hijos:
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
Esto también funciona para componentes definidos por el usuario:
function FormattedDate(props) {
return <h2>It is {props.date.toLocaleTimeString()}.</h2>;
}
<FormattedDate date={this.state.date} />;
Ejercicios:
- Crear un componente de clase que muestre un contador y tenga dos botones: uno de incrementar y otro de decrementar.
- Crear un componente de clase que muestre un
string
vacio y tenga dos botones: uno de incrementar y otro de decrementar. Cuandos se pinche el de incrementar añadirá una letra al string y cuando se pinche en el de decrementar quitará una letra. - Refactorizar el primer ejemplo para que:
- Los botones sean componentes a parte.
- El texto del contador sea un componente a parte
- El componente Counter sea quien tenga el estado y las funciones que lo actualizan y se lo pase a sus hijos.