Reputation: 603
I'm making a Nextjs flashcard app. I'm passing a deck structure like this:
const deck = {
title: 'React 101',
flashcards: [flashcardOne, flashcardTwo],
};
as props to the Deck
component. This component shows the first card in flashcards and a "next" button to increment the index and showing the next card in flashcards.
The Card
component is very simple and shows the front and the back of the card depending of the state front
.
This is what I got so far and it's working but if I click "next" when the card is showing the answer (flashcard.back
), the next card is going to appear with the answer. And I'm not sure why, isn't the Card
component re rendering when I click "next"? And if the component re renders, front
is going to be set to true?
export default function Deck({ deck }) {
const [cardIndex, setCardIndex] = useState(0);
const { title, flashcards } = deck;
return (
<div className={styles.container}>
<main className={styles.main}>
<h1 className={styles.title}>{title}</h1>
{cardIndex < flashcards.length ? (
<>
<div className={styles.grid}>
<Card flashcard={flashcards[cardIndex]} />
</div>
<button onClick={() => setCardIndex((cardIndex) => cardIndex + 1)}>
Next
</button>
</>
) : (
<>
<div>End</div>
<button>
<Link href='/'>
<a>Go to Home</a>
</Link>
</button>
<button onClick={() => setCardIndex(0)}>Play again</button>
</>
)}
</main>
</div>
);
}
export function Card({ flashcard }) {
const [front, setFront] = useState(true);
return (
<>
{front ? (
<div
className={`${globalStyles.card} ${styles.card}`}
onClick={() => setFront(false)}
>
<p className={styles.front}>{flashcard.front}</p>
</div>
) : (
<div
className={`${globalStyles.card} ${styles.card}`}
onClick={() => setFront(true)}
>
<p className={styles.back}>{flashcard.back}</p>
</div>
)}
</>
);
}
Upvotes: 0
Views: 47
Reputation: 371138
When state changes, the card will re-render, but it will not re-mount. So, existing state will not be reset.
Call setFront(true)
when the flashcard prop has changed:
const [front, setFront] = useState(true);
useLayoutEffect(() => {
setFront(true);
}, [flashcard]);
I'm using useLayoutEffect
instead of useEffect
to ensure front
gets set ASAP, rather than after a paint cycle (which could cause flickering).
You can also significantly slim down the Card
JSX:
export function Card({ flashcard }) {
const [front, setFront] = useState(true);
const face = front ? 'front' : 'back';
return (
<div
className={`${globalStyles.card} ${styles.card}`}
onClick={() => setFront(!front)}
>
<p className={styles[face]}>{flashcard[face]}</p>
</div>
);
}
Upvotes: 2
Reputation: 167220
Okay, I guess I had the same issue. Since you're using functional components, and you're re-using the same component or in better words, you're not unmounting and remounting the component really, you're just changing the props, this happens. For this, you need to do useEffect()
and then setFront(true)
.
Here's the code I used in my App.
useEffect(() => {
setFront(true);
}, [flashcard]);
This is what I have used in my Word.js
file.
Upvotes: 1