Info |
---|
I was putting together some code to demonstrate how modifying a state variable causes a component to rerender. All was going will until I put a timer on the component, that got the current time and displayed it in the page. That worked fine example 1, but it all changed when I wanted to keep a list of all the times and print them all out each time a new time was added, things a got a little weird, example 2. |
...
If you want data to persist across component renders, you need to use useRef()
. We'll cover this in another section
Example 2
All of the above explains why the following code will not run as expected if you don’t use the useRef() callackcallback
Code Block | ||
---|---|---|
| ||
import React, { useState } from 'react' import { useEffect } from 'react'; import { useRef } from 'react'; function getTheTime() { let currentDate = new Date(); let currentTime = currentDate.getHours() + ":" + currentDate.getMinutes() + ":" + currentDate.getSeconds(); return currentTime; } export default function ContainerFcWuE(){ const [simpleField, setSimpleField] = useState(''); const [containerState, setContainerState] = useState([getTheTime()]); /* Without these two lines below, this code will not work as expected * The problem is, every time a state field changes the function is * is re-executed in it's entirety, so the state fields are completely * reset. I know I was suprised by this fact! * * useRef allows you to create a mutable value that does not cause a * re-render when it is changed. * * If you remove containerRef and containerRef.current and replace * containerRef.current with containerState, you will see the problem. * * A description of this problem and a solution can be found at * https://stackoverflow.com/questions/56511176/state-being-reset */ const containerRef = useRef([]); containerRef.current = containerState; const addToList = name => { console.log(`Current array: ${containerRef.current}`); const update = [...containerRef.current, name]; console.log(`Updated array: ${update}`); setContainerState(update); } const addTimeToList = () => { addToList(getTheTime()) }; const getDisplayData = () => { const cdata = containerState.map((item, index) => <li key={index}>{item}</li>); return cdata; } // This useEffect will only run once when the compone is mounted useEffect(()=>{ setInterval(()=>{ addTimeToList() }, 10000); }, []) // This useEffect will run every time the component re-renders useEffect(() =>{ console.log(getDisplayData()) }) // This useEffect will run every time the simpleField changes useEffect(() =>{ console.log(simpleField); }, [simpleField]); // This useEffect will run every time the component is unmounted useEffect(() =>{ return ()=>console.log("unmounting the ContainerFcWuE"); }); return ( <> Type something (open dev tools/console to see behaviour)<input type='text' value={simpleField} onChange={(e) => setSimpleField(e.target.value)} /> <div onClick={e => setSimpleField("YYY")}> <h2>[fcwue] Click Me</h2> </div> <ul>{getDisplayData()}</ul> </> ) } |