Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

277
Views
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 answers
Answer question

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 Report

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 Report

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 Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!