Trabajar con la API de contexto de reacción

Publicado: 2022-03-11

La API de contexto de React ha existido como una función experimental durante un tiempo, pero solo en la versión 16.3.0 de React se volvió seguro para usar en producción. El siguiente artículo le mostrará dos aplicaciones básicas de la tienda web, una creada con la API de contexto y otra sin ella.

Esta nueva API resuelve un problema importante: la perforación de puntales . Incluso si no está familiarizado con el término, si ha trabajado en una aplicación React.js, probablemente le haya pasado. La perforación de prop es el procesamiento de obtener datos del componente A al componente Z pasándolos a través de múltiples capas de componentes intermedios de React. El componente recibirá accesorios indirectamente y usted , el desarrollador de React, tendrá que asegurarse de que todo funcione correctamente.

Exploremos cómo manejaría problemas comunes sin la API React Context,

Aplicación.js

 class App extends Component { state = { cars: { car001: { name: 'Honda', price: 100 }, car002: { name: 'BMW', price: 150 }, car003: { name: 'Mercedes', price: 200 } } }; incrementCarPrice = this.incrementCarPrice.bind(this); decrementCarPrice = this.decrementCarPrice.bind(this); incrementCarPrice(selectedID) { // a simple method that manipulates the state const cars = Object.assign({}, this.state.cars); cars[selectedID].price = cars[selectedID].price + 1; this.setState({ cars }); } decrementCarPrice(selectedID) { // a simple method that manipulates the state const cars = Object.assign({}, this.state.cars); cars[selectedID].price = cars[selectedID].price - 1; this.setState({ cars }); } render() { return ( <div className="App"> <header className="App-header"> <img src={logo} className="App-logo" alt="logo" /> <h1 className="App-title">Welcome to my web store</h1> </header> {/* Pass props twice */} <ProductList cars={this.state.cars} incrementCarPrice={this.incrementCarPrice} decrementCarPrice={this.decrementCarPrice} /> </div> ); } }

Lista de productos .js

 const ProductList = props => ( <div className="product-list"> <h2>Product list:</h2> {/* Pass props twice */} <Cars cars={props.cars} incrementCarPrice={props.incrementCarPrice} decrementCarPrice={props.decrementCarPrice} /> {/* Other potential product categories which we will skip for this demo: */} {/* <Electronics /> */} {/* <Clothes /> */} {/* <Shoes /> */} </div> ); export default ProductList;

Coches.js

 const Cars = props => ( <Fragment> <h4>Cars:</h4> {/* Finally we can use data */} {Object.keys(props.cars).map(carID => ( <Car key={carID} name={props.cars[carID].name} price={props.cars[carID].price} incrementPrice={() => props.incrementCarPrice(carID)} decrementPrice={() => props.decrementCarPrice(carID)} /> ))} </Fragment> );

Coche.js

 const Cars = props => ( <Fragment> <p>Name: {props.name}</p> <p>Price: ${props.price}</p> <button onClick={props.incrementPrice}>&uarr;</button> <button onClick={props.decrementPrice}>&darr;</button> </Fragment> );

De acuerdo, esta no es la mejor manera de manejar sus datos, pero espero que demuestre por qué la perforación de puntales apesta . Entonces, ¿cómo puede la API de contexto ayudarnos a evitar esto?

Introducción a la tienda web Context

Refactoricemos la aplicación y demostremos lo que puede hacer. En pocas palabras, la API de contexto le permite tener un almacén central donde viven sus datos (sí, como en Redux). La tienda se puede insertar en cualquier componente directamente. ¡Puedes eliminar al intermediario!

Ejemplo de dos flujos de estado: uno con React Context API y otro sin

La refactorización es bastante fácil: no tenemos que hacer ningún cambio en la forma en que se estructuran los componentes. Sin embargo, necesitamos crear algunos componentes nuevos: un proveedor y un consumidor.

1. Inicializar el contexto

Primero, necesitamos crear el contexto, que luego podemos usar para crear proveedores y consumidores.

MiContexto.js

 import React from 'react'; // this is the equivalent to the createStore method of Redux // https://redux.js.org/api/createstore const MyContext = React.createContext(); export default MyContext;

2. Crear el Proveedor

Una vez hecho esto, podemos importar el contexto y usarlo para crear nuestro proveedor, al que llamaremos MyProvider. En él, inicializamos un estado con algunos valores, que puede compartir a través de la propiedad de valor de nuestro componente de proveedor. En nuestro ejemplo, compartimos this.state.cars junto con un par de métodos que manipulan el estado. Piense en estos métodos como reductores en Redux.

MiProveedor.js

 import MyContext from './MyContext'; class MyProvider extends Component { state = { cars: { car001: { name: 'Honda', price: 100 }, car002: { name: 'BMW', price: 150 }, car003: { name: 'Mercedes', price: 200 } } }; render() { return ( <MyContext.Provider value={{ cars: this.state.cars, incrementPrice: selectedID => { const cars = Object.assign({}, this.state.cars); cars[selectedID].price = cars[selectedID].price + 1; this.setState({ cars }); }, decrementPrice: selectedID => { const cars = Object.assign({}, this.state.cars); cars[selectedID].price = cars[selectedID].price - 1; this.setState({ cars }); } }} > {this.props.children} </MyContext.Provider> ); } }

Para que el proveedor sea accesible para otros componentes, debemos envolver nuestra aplicación con él (sí, al igual que en Redux). Mientras estamos en eso, podemos deshacernos del estado y los métodos porque ahora están definidos en MyProvider.js.

Aplicación.js

 class App extends Component { render() { return ( <MyProvider> <div className="App"> <header className="App-header"> <img src={logo} className="App-logo" alt="logo" /> <h1 className="App-title">Welcome to my web store</h1> </header> <ProductList /> </div> </MyProvider> ); } }

3. Crear el Consumidor

Tendremos que importar el contexto nuevamente y envolver nuestro componente con él, lo que inyecta el argumento de contexto en el componente. Después, es bastante sencillo. Usas el contexto , de la misma manera que usarías accesorios . Contiene todos los valores que hemos compartido en MyProducer, ¡solo necesitamos usarlo!

Coches.js

 const Cars = () => ( <MyContext.Consumer> {context => ( <Fragment> <h4>Cars:</h4> {Object.keys(context.cars).map(carID => ( <Car key={carID} name={context.cars[carID].name} price={context.cars[carID].price} incrementPrice={() => context.incrementPrice(carID)} decrementPrice={() => context.decrementPrice(carID)} /> ))} </Fragment> )} </MyContext.Consumer> );

¿Qué olvidamos? ¡La lista de productos! Aquí es donde el beneficio se hace evidente. No pasamos ningún dato o método. El componente se simplifica porque solo necesita renderizar algunos componentes.

ProductList.js

 const ProductList = () => ( <div className="product-list"> <h2>Product list:</h2> <Cars /> {/* Other potential product categories which we will skip for this demo: */} {/* <Electronics /> */} {/* <Clothes /> */} {/* <Shoes /> */} </div> );

En el transcurso de este artículo, hice algunas comparaciones entre Redux y la API de contexto. Una de las mayores ventajas de Redux es el hecho de que su aplicación puede tener una tienda central a la que se puede acceder desde cualquier componente. Con la nueva API de contexto, tiene esa funcionalidad de forma predeterminada. Se ha hecho mucha publicidad de que la API de Contexto dejará obsoleto a Redux.

Esto podría ser cierto para aquellos de ustedes que solo usan Redux para sus capacidades de almacenamiento central. Si esa es la única función para la que la estaba usando, ahora puede reemplazarla con la API de contexto y evitar la exploración de accesorios sin usar bibliotecas de terceros.

Si está interesado en medir y optimizar el rendimiento de su aplicación React, lea Una guía para optimizar el rendimiento de React del compañero Toptaler William Wang.