I am a beginner and i have a structure like this:
<CountContext.Provider value={{ contextCount, setContextCount }}>
<Wrapper />
<Bar />
</CountContext.Provider>
Inside the wrapper I have 12 cards, each with a click counter.
The bar contains a counter of all clicks, implemented through the context, as well as a reset button.
How can I, without using third-party libraries, reset not only the general counter, but also reset each individual counter on the cards?
More details:
export const CountContext = createContext();
const App = () => {
const [contextCount, setContextCount] = useState(0);
return (
<div className="app">
<CountContext.Provider value={{ contextCount, setContextCount }}>
<Wrapper />
<Bar />
</CountContext.Provider>
</div>
);
};
export default App;
const Wrapper = () => {
return (
<div className="wrapper">
{cards.map((card) => (
<Card color={card.color} key={card.id} />
))}
</div>
);
};
export default Wrapper;
const Card = ({ color }) => {
const { contextCount, setContextCount } = useContext(CountContext);
const [count, setCount] = useState(0);
const clickOnCard = () => {
setCount(count + 1);
setContextCount((prev) => prev + 1);
};
return (
<div
className="card"
style={{ backgroundColor: color }}
onClick={clickOnCard}
>
{count}
</div>
);
};
export default Card;
const Bar = () => {
const { contextCount, setContextCount, styleBtn, setStyleBtn } =
useContext(CountContext);
const reset = () => {
setContextCount(0);
};
return (
<div className="bar">
Total Count:{contextCount}
<button className="btn" onClick={reset}>
Reset
</button>
</div>
);
};
I try useEffect, context, if-else, conditional render and some other ways.
2
Answers
To achieve this without using third-party libraries, you can follow these steps:
Modify Context:
Update your
CountContext
to include a function for resetting individual card counters. This function can be passed down to each card.Update Cards:
Pass the reset function down to each card and use it to reset the individual counters.
Update Wrapper:
In the
Wrapper
component, render multiple instances of theCard
component and pass the reset function to each card.Update Bar:
In the
Bar
component, use the context to access theresetAllCounters
function and implement the reset button.Now, when you click the "Reset All Counters" button in the
Bar
component, it will reset both the general counter and the individual counters on each card. Adjust the logic inside theresetCardCounter
function in theWrapper
component based on your requirements for resetting individual card counters.This is a classic "state hoisting" problem. The state of the counters for each card is currently kept in the
Card
component itself, so they are not within the scope of the context to be able to reset.Option A – State Hoisting
The answer in these situations is usually to move that state upwards to bring it into scope.
To do this, we can store each number for each card inside the context provider in the form of a map where the key is the card ID and the value is the current count for that card. We will then pass accessors down so the cards can use the count and also provide a method to reset them.
Wrapper
is almost the same apart from we pass down the ID as a normal prop so the card can use it to identify the right count.Card
now relies on the context for its current value and to update it.Bar
just needs rewiring a little for the new naming:If you need to deal with the total count reducing if one of the
Card
unmounts you could achieve it using effects.Option B – Forced Reset
This is far simpler, but must be used with caution and not become the default way of doing this without considering the trade-offs each time.
In this solution, you utilise React’s
key
property. When this changes on a given component, it causes that component and all of its children to completely unmount and remount, which has the effect of clearing their state also.The reason for being cautious is this means you are totally giving up on React performant diffing algorithm and just building up the DOM all over again when they hit reset. For trivial apps though, you might not care.
Also, it will wipe out any other transient state you have in the children. It’s a "sledgehammer" approach. But it can be useful sometimes if you don’t care about the above impacts and it has a bonus in that you don’t have to manage the resets yourself.
In
App
:In
Bar