I am new to React and facing an issue with updating the state of a functional component.
I have a component TinderCards.js
like below, which basically takes in a list of objects from a context provider and for each object, creates a TinderCard
(https://www.npmjs.com/package/react-tinder-card) which is supposed to update the count
when swiped (onSwipe
).
However, what happens is that every time a card gets swiped, the count
gets increased to 1 and then doesn't go any further.
E.g.
Initial count value: 0
Swipe first card => count = 1
Swipe second card => count = 1 (should be 2)
Swipe third card => count = 1 (should be 3)
I have tried including the count
value as both part of the Component's state and as part of a global context called ResultsContext.js
, but both lead to the same behaviour.
NOTE that the button with class test-btn
behaves EXACTLY how I want at onClick
. However, the onSwipe
for the TinderCard
Component doesn't, even though it's basically the same code and it's driving me crazy.
TinderCards.js
import React, { useState, useEffect, useContext } from "react";
import TinderCard from "react-tinder-card";
import StocksContext from "./StocksContext";
import ResultsContext from "./ResultsContext";
import "./Tindercards.css";
function TinderCards(props) {
// const [count, setCount] = useState(0);
const { count, setCount } = useContext(ResultsContext);
const { stocks, setStocks } = useContext(StocksContext);
return (
<div>
<div className="tinderCards__cardContainer">
<div>
Count: {count}
<button className="test-btn" onClick={() => setCount(count + 1)}>Increment</button>
</div>
<div className="main__container">
{stocks.map((stock) => (
<TinderCard
onSwipe={() => setCount(count + 1)}
className="swipe"
key={stock.name}
preventSwipe={["up", "down"]}
>
<div className="card">
<div style={{ height: "100%" }}>
<img
src={stock.url}
style={{
objectFit: "contain",
height: "100%",
width: "100%",
backgroundColor: "white",
}}
/>
</div>
</div>
<h3 style={{ backgroundColor: "white" }}>${stock.name}</h3>
</TinderCard>
))}
</div>
</div>
</div>
);
}
export default TinderCards;
I appreciate any help! Thank you very much!
Each count
variable inside the onSwipe
handler(s):
() => setCount(count + 1)
is a closure that contains the value of count
at the point they were rendered - which is 0. One of those functions running doesn't actually change the value of the count
variable - it simply triggers a rerender of the parent component. Those other children still retain their onSwipe
handler that simply sets the count to 2 (1 more than the value of count
when they were rendered) - which is what you're seeing.
The simple solution here is to pass an update function to setCount
, rather than a hardcoded value:
onSwipe={() => setCount(prevCount => prevCount + 1)}
This will ensure that the "latest value" of the count
state is always read, and incremented by 1.