I am currently learning react and have stumbled upon a section in their ‘tutorial’ section called ‘Describing the UI’. I can’t grasp my mind around understanding a subsection called ‘Keeping components pure’.
The thing I don’t understand is this:
Given this code:
let guest = 0;
function Cup() {
// Bad: changing a preexisting variable!
guest = guest + 1;
return <h2>Tea cup for guest #{guest}</h2>;
}
export default function TeaSet() {
return (
<>
<Cup />
<Cup />
<Cup />
</>
);
}
Why does the output look like this:
Tea cup for guest #2
Tea cup for guest #4
Tea cup for guest #6
And not like this:
Tea cup for guest #1
Tea cup for guest #2
Tea cup for guest #3
I tried to figure it out myself but I can’t.
>Solution :
This likely happens becuase, in development mode, React renders everything twice on mount. You can witness this by adding the following into Cup:
useEffect(() => {
console.log('cup');
}, []);
Even though the useEffect has no dependencies and should only run once, you would see "cup" printed twice per Cup.
With that in mind, each Cup component is being rendered twice. You have three, and therefore the final render displays the result after the second render for each, settling on 2, 4, and 6.
However, keeping state in a global variable like this is not the move, and leads to unexpected behaviour. Given that, as you mention, Cup should be a "pure" component, its dependency on the global variable guest is counter-intuitive.
A better approach would be to tell Cup to take some input, and always display that input. This is called props (short for properties), and allows you to render your cups purely based on a state, which is managed elsewhere. A serving suggestion:
function Cup({ guest }) {
// we destructure the guest from the props
return <h2>Tea cup for guest #{guest}</h2>;
}
export default function TeaSet() {
return (
<>
<Cup guest={1} />
<Cup guest={2} />
<Cup guest={3} />
</>
);
}
This opens up the possibility of keeping a state of guests in TesSet then looping over it. A more advanced example:
function TeaSet() {
const [guests, setGuests] = useState([1, 2, 3]);
return (
<>
{guests.map(guest => <Cup key={guest} guest={guest} />)}
</>
);
}
This allows you to manipulate the state from within TeaSet without it affecting individual cups for existing guests, and without relying on any global variable, which may be handled in unexpected ways.