Empresas
Empleos
  • Sobre nosotros
  • Soluciones
    • Publicación de vacantes
      Publica tu vacante y recibe candidatos calificados en 48h.
    • Evaluación de candidatos
      500+ pruebas técnicas y psicológicas, más anti-fraude.
    • Headhunting
      Búsqueda ejecutiva a la medida de principio a fin.
    • Nómina + EOR
      Dispersión de nómina y EOR en más de 15 países de LATAM.
  • Precios
  • Empleos

0

278
Vistas
useReducer: acción de envío, muestra el estado en otro componente y actualiza el estado cuando se envía la acción

Tengo un problema que no puedo resolver. Estoy creando una aplicación de reacción de comercio electrónico y uso useReducer y useContext para la administración del estado. El cliente abre un producto, elige una cantidad de artículos y luego hace clic en el botón "Agregar al carrito" que envía una acción. Esta parte está funcionando bien y comienza el problema. No sé cómo mostrar y actualizar en el componente Navbar.js una cantidad total de productos en el carrito. Se muestra después de los cambios de ruta, pero quiero que se actualice al hacer clic en el botón Agregar al carrito. Probé useEffect pero no funciona.

el estado inicial se ve así

 const initialState = [ { productName: '', count: 0 } ]

AddToCart.js funciona bien

 import React, { useState, useContext } from 'react' import { ItemCounterContext } from '../../App' function AddToCart({ product }) { const itemCounter = useContext(ItemCounterContext) const [countItem, setCountItem] = useState(0) const changeCount = (e) => { if (e === '+') { setCountItem(countItem + 1) } if (e === '-' && countItem > 0) { setCountItem(countItem - 1) } } return ( <div className='add margin-top-small'> <div className='add-counter' onClick={(e) => changeCount(e.target.innerText)} role='button' > - </div> <div className='add-counter'>{countItem}</div> <div className='add-counter' onClick={(e) => changeCount(e.target.innerText)} role='button' > + </div> <button className='add-btn btnOrange' onClick={() => itemCounter.dispatch({ type: 'addToCart', productName: product.name, count: countItem })} > Add to Cart </button> </div> ) } export default AddToCart

Navbar.js es donde tengo un problema

 import React, { useContext } from 'react' import { Link, useLocation } from 'react-router-dom' import NavList from './NavList' import { StoreContext, ItemCounterContext } from '../../App' import Logo from '../Logo/Logo' function Navbar() { const store = useContext(StoreContext) const itemCounter = useContext(ItemCounterContext) const cartIcon = store[6].cart.desktop const location = useLocation() const path = location.pathname const itemsSum = itemCounter.state .map((item) => item.count) .reduce((prev, curr) => prev + curr, 0) const totalItemsInCart = ( <span className='navbar__elements-sum'> {itemsSum} </span> ) return ( <div className={`navbar ${path === '/' ? 'navTransparent' : 'navBlack'}`}> <nav className='navbar__elements'> <Logo /> <NavList /> <Link className='link' to='/cart'> <img className='navbar__elements-cart' src={cartIcon} alt='AUDIOPHILE CART ICON' /> {itemsSum > 0 ? totalItemsInCart : null} </Link> </nav> </div> ) } export default Navbar
over 4 years ago · Santiago Trujillo
3 Respuestas
Responde la pregunta

0

Bueno, ItemCounterContext es importante para este problema, simplemente ignore StoreContext, es para imágenes... Aquí hay una función reductora.

 export const reducer = (state, action) => { // returns -1 if product doesn't exist const indexOfProductInCart = state.findIndex((item) => item.productName === action.productName) const newState = state switch (action.type) { case 'increment': { if (indexOfProductInCart === -1) { newState[state.length] = { productName: action.productName, count: state.count + 1 } return newState } newState[indexOfProductInCart] = { productName: action.productName, count: state.count + 1 } return newState } case 'decrement': { if (indexOfProductInCart === -1) { newState[state.length] = { productName: action.productName, count: state.count - 1 } return newState } newState[indexOfProductInCart] = { productName: action.productName, count: state.count - 1 } return newState } case 'addToCart': { if (indexOfProductInCart === -1) { newState[state.length] = { productName: action.productName, count: action.count } return newState } newState[indexOfProductInCart] = { productName: action.productName, count: action.count } return newState } case 'remove': return state.splice(indexOfProductInCart, 1) default: return state } }

Y aquí está App.js donde comparto el estado con otros componentes

 import React, { createContext, useMemo, useReducer } from 'react' import { BrowserRouter as Router, Routes, Route } from 'react-router-dom' import Navbar from './components/Navbar/Navbar' import Homepage from './pages/Homepage/Homepage' import Footer from './components/Footer/Footer' import ErrorPage from './pages/ErrorPage/ErrorPage' import SelectedCategory from './pages/SelectedCategory/SelectedCategory' import SingleProduct from './pages/SingleProduct/SingleProduct' import ScrollToTop from './services/ScrollToTop' import store from './services/data.json' import { reducer } from './services/ItemCounter' import './scss/main.scss' export const StoreContext = createContext(store) export const ItemCounterContext = createContext() function App() { const initialState = [{ productName: '', count: 0 }] const [state, dispatch] = useReducer(reducer, initialState) const counter = useMemo(() => ({ state, dispatch }), []) return ( <div className='app'> <StoreContext.Provider value={store}> <ItemCounterContext.Provider value={counter}> <Router> <ScrollToTop /> <Navbar /> <Routes> <Route path='/' element={<Homepage />} /> <Route path='/:selectedCategory' element={<SelectedCategory />} /> <Route path='/:selectedCategory/:singleProduct' element={<SingleProduct />} /> <Route path='*' element={<ErrorPage />} /> </Routes> <Footer /> </Router> </ItemCounterContext.Provider> </StoreContext.Provider> </div> ) } export default App
over 4 years ago · Santiago Trujillo Denunciar

0

El problema está en su reductor, particularmente donde asigna el estado anterior al estado newState para hacer mutaciones y devolver el estado actualizado. En JavaScript, los no primitivos se refieren por dirección y no por valor. Dado que su initialState que es una matriz, no es primitivo, por lo que cuando asigna un no primitivo a una nueva variable, esta variable solo apunta a la matriz existente en la memoria y no crea una nueva copia. Y, en React, las actualizaciones se activan/transmiten solo cuando se reconstruye un estado (así es como React entiende que hay una actualización) y no muta suavemente. Cuando muta y devuelve newState , básicamente está mutando el state existente y no haciendo que se reconstruya. Una solución rápida para esto sería copiar su state en newState y no simplemente asignarlo. Esto podría hacerse usando el operador de propagación ( ... ).
En su función reductora, cambie:

 const newState = state

para

 const newState = [...state]

Su función reductora debería verse así:

 export const reducer = (state, action) => { // returns -1 if product doesn't exist const indexOfProductInCart = state.findIndex((item) => item.productName === action.productName) const newState = [...state] //Deep-copying the previous state switch (action.type) { case 'increment': { if (indexOfProductInCart === -1) { newState[state.length] = { productName: action.productName, count: state.count + 1 } return newState } newState[indexOfProductInCart] = { productName: action.productName, count: state.count + 1 } return newState } case 'decrement': { if (indexOfProductInCart === -1) { newState[state.length] = { productName: action.productName, count: state.count - 1 } return newState } newState[indexOfProductInCart] = { productName: action.productName, count: state.count - 1 } return newState } case 'addToCart': { if (indexOfProductInCart === -1) { newState[state.length] = { productName: action.productName, count: action.count } return newState } newState[indexOfProductInCart] = { productName: action.productName, count: action.count } return newState } case 'remove': return state.splice(indexOfProductInCart, 1) default: return state } }
over 4 years ago · Santiago Trujillo Denunciar

0

Sé exactamente de qué estás hablando, pero el problema en el reductor es que solo los métodos mutativos funcionan en el estado. Los métodos inmutables como .slice(), .concat() o incluso el operador de propagación [...state] no funcionan y no sé por qué :( probé ambas respuestas, pero dispatch(action) no funciona cambiar el estado Tal vez el estado inicial es el problema, intentaré ponerlo así

estadoInicial = { carrito: [nombreProducto: '', cuenta: 0] }

over 4 years ago · Santiago Trujillo Denunciar
Responde la pregunta
Encuentra empleos remotos

¡Descubre la nueva forma de encontrar empleo!

Top de empleos
Top categorías de empleo
Empresas
Publicar vacante Precios Comercial
Legal
Términos y condiciones Política de privacidad
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomiéndame algunas ofertas
Necesito ayuda