1

I am trying to do some kind of online shop for myself and I got a problem. I want to render my shopping cart size in the NavBar component (which is on every page).

I created a Cart Items service where I put all my added items, and it also has functions to addItem, removeItem, getCart, getCartSize.

When I click on Add/Remove on specific product, I would like to do that the value on NavBar with cart size would be changing depending on the cart size (from getCartSize method). I already tried to use useEffect hook, but it does not recognize when the value of cartSize is changed, how can I do that?

This is what I have done already.

navbar.jsx:

//...
//...
import {getTotalCount} from '../../services/myCart';

export default function Navbar() {
  // ...
  const [count, setCount] = useState();  

  useEffect(() => {
    setCount(getTotalCount());
    console.log('counto useeffect');
  },[getTotalCount()]);
  //},[getTotalCount]); doesn'work also.
  //},[count]); doesn'work also.

  return (
    <>
        <header className="navbar">
                <Link className="navbar__list-item__home-icon" to="/"><span><FaHome/></span></Link>
                <Link className="navbar__list-item" to="/products">Products</Link>            
                <h2>cart size--> {count}</h2>
                <Link className="navbar__list-item__cart-img" to="shopping-cart"><span><FaShoppingCart/></span></Link>   
        </header>
    </>
  );
}

myCart.js all functions work fine and I call them when I click add/remove button in on my component in the products page.

var InTheCart = [

];
var totalCount = 0;

export function AddToCart(item) {
    // ... logic
    totalCount++;
}

export function ContainsInTheCart(id) {
    return InTheCart.findIndex(x=> x.item.id == id) != -1 ? true: false;   
}

export function RemoveFromCart(id) {
   // ... logic
        totalCount--;
}

export function getCart() {
    return InTheCart;
}

export function getTotalCount() {
    return totalCount;
}


Dignity Dignity
  • 151
  • 2
  • 11
  • //},[count]) will cause infinit loop beacuse every time useState happens the rerender will be called – Afsanefda Dec 05 '19 at 18:16
  • so the initial value is 0 ? I mean the getTotalFunction always return 0. is it right ? – Afsanefda Dec 05 '19 at 18:18
  • yes in myCart.js as you can see totalCount is 0, at the beginning – Dignity Dignity Dec 05 '19 at 18:21
  • Does this answer your question? [In React useEffect does not update after the value has changed from other component](https://stackoverflow.com/questions/59198761/in-react-useeffect-does-not-update-after-the-value-has-changed-from-other-compon) –  Jan 22 '20 at 16:12

2 Answers2

1

React is called react because it re-renders when it reacts to something that changed.

For that to happen, the react components need to know, somehow, that something changed.

In the code example you show, the totalCount (which global access provided by exported function getTotalCount) is independent from react component tree, so that data can change without react knowing it.

So, the question is: how to make your react component aware of those changes?

A few mechanisms exist, here are a few of them:

  1. Use a global state manager like redux, with redux actions and reducer to mutate the state and useSelector in the component that will make them aware of any change, and rerender

  2. React context which will hold the cart state. That context can export both that data, and th function that will mutate the data, so they can be shared between components that have access to that context provider.

Your context could be used in the component that way:

  const cartContext = useContext(CartContext)
  const { cartState, addToCart, removeFromCart } = cartContext
  const { totalCount, inTheCart } = cartState

  [...somwhere later...]
  <ul><li onClick={() => removeFromCart(index)}>{inTheCart[index].name}</li></ul>
  <footer>{`${totalCount} items`}</footer>

I let you build the context object around your existing functions ;)

Pandaiolo
  • 11,165
  • 5
  • 38
  • 70
1

A simple solution is to lift the state up. Create the state in your layout file from where Header and Cart can access the count as Props. useEffect will be invoked whenever there is a change in the state or props of a component.

Muhammad Zeeshan
  • 4,608
  • 4
  • 15
  • 41
  • That would work if all myCart.js stuff is done with useState API in that component. In that case NavBar will not need any effect , they can just use the totalCount prop. It will rerender whenever that changes. Limitation is that if another component need that state, it must be a direct child of that parent component. Or, in case of a lot of intermediary components, you need to "drill" those props everywhere. Still a good solution for simple use case. – Pandaiolo Dec 05 '19 at 18:56
  • Yes you are right. It all depends on your scenario. If want to access the count at multiple places and want to get rid of drilling the props then using `Context API` or `Redux` will be a good option. – Muhammad Zeeshan Dec 06 '19 at 06:09