js -react ejemplo useReducer con fetch y bootstrap 5
React hook useReduce con fetch

En react para trabajar con el estado solemos recurrir a useState y useEffect. Esto está bien cuando es un estado sencillo como el siguiente ejemplo:
function Ejemplo() {
const [nombre, setNombre] = useState("")
useEffect(()=>{
setNombre("Eduardo A. F.")
},[])
return (
<span><b>Nombre:</b> {nombre}</span>
)
}
Cuando tenemos que manejar un estado más complejo lo más conveniente es recurrir a un gestor de estado global; concretamente a una función actualizadora de estado llamada
función reductora o reducer.
Esta función recibe dos parámetros. El nuevo estado y la acción. En el ejemplo coincide que las dos acciones actualizan de la misma forma el estado.
Algo como lo siguiente:
const fn_reducer = (state, action) => {
switch(action.type) {
case ACTIONS.FETCH_SUCCESS:
case ACTIONS.FETCH_ERROR:
return {
error: action.error,
loading: action.loading,
products: action.payload
}
default:
return state
}
}
El código
Este snippet carga el listado que se muestra en la imagen anterior.
import { useReducer, useEffect } from "react"
import "../css/reducer.css"
const URL_PRODUCTS = "https://json.theframework.es/index.php?getfile=app_product.json"
const ACTIONS = {
FETCH_SUCCESS: "FETCH_SUCCESS",
FETCH_ERROR: "FETCH_ERROR"
}
//nuestro estado más complejo
const stateproducts = {
loading: true,
error: "",
products: []
}
const fn_reducer = (state, action) => {
switch(action.type) {
case ACTIONS.FETCH_SUCCESS:
case ACTIONS.FETCH_ERROR:
return {
error: action.error,
loading: action.loading,
products: action.payload
}
default:
return state
}
}
function Reducer() {
//el estado y su setter que es un dispatcher
//creamos unas constantes que son "copias" del reducer y el estado complejo
const [state, dispatch] = useReducer(fn_reducer, stateproducts)
/*
//usando fetch como promesa, hace lo mismo que con async y await
useEffect(() => {
fetch(URL_PRODUCTS)
.then(response => response.json())
.then(data => dispatch({
type: ACTIONS.FETCH_SUCCESS,
error: "",
payload: data
}))
.catch(ex => dispatch({
type: ACTIONS.FETCH_ERROR,
error: ex.message,
payload: []
}))
}, [])
*/
//usando fetch con async y await
useEffect(()=>{
(async () => {
try {
const response = await fetch(URL_PRODUCTS)
if( !response.ok ) {
throw new Error(`HTTP error! status: ${response.status}`)
}
const data = await response.json()
dispatch({
type: ACTIONS.FETCH_SUCCESS,
error: "",
payload: data
})
}
catch (ex) {
console.log(ex.message)
dispatch({
error: ex.message,
type: ACTIONS.FETCH_ERROR,
payload: []
})
}
})()
},[])
return (
<>
<div className="center">
<h2>Productos</h2>
<span className="status">
Estado: {
state.error ?
<h1 className="badge bg-danger">{state.error}</h1>
:
<h1 className="badge bg-success">Ok</h1>
}
</span>
</div>
<div className="grid mt-3">
<div className="center">
<ul className="list-group">
{state.loading ?
"Loading"
:
state.products.map(product =>
<li key={product.id} className="list-group-item d-flex justify-content-between align-items-start">
<div className="ms-2 me-auto">
<div className="fw-bold">{product.name}</div>
{product.description}
</div>
<span className="badge bg-primary rounded-pill">{product.id}</span>
</li>
)
}
</ul>
</div>
</div>
</>
)
}
export default Reducer
import TestReducer from "./components/reducer"
function App() {
return <TestReducer />
}
export default App
El código fuente completo lo dejo en mi Github
Autor: Eduardo A. F.
Publicado: 16-07-2021 23:11