I'm trying to pass a state generated inside an async function, which itself is inside useState. I learned that useRef is probably the best way to solve this problem because it can reference mutable state, and in the process learned about React-useStateRef, which finally solved another problem where the state inside my main component was never updated Problem (keeps getting "too many renders" errors). So it essentially acts as useRef and useState rolled into one.
However, although my state finally updated, it still wasn't passed to my Canvas component. I'm trying to update a canvas's BG graph based on the temperatures obtained from a dataset.
import { useEffect } from 'react'; import useState from 'react-usestateref'; import { getServerData } from './serviceData'; import "./App.css" import Canvas from './components/Canvas'; function App() { const [dataset, setDataset] = useState(null); const [units, setUnits] = useState('metric'); // canvas background graphic stuff var [currentBG, setCurrentBG, currentBGref] = useState(null); useEffect(() => { const fetchServerData = async () => { const data = await getServerData(city, units); setDataset(data); function updateBG() { const threshold = units === "metric" ? 20 : 60; if (data.temp <= threshold) { setCurrentBG('snow'); } else { setCurrentBG('sunny'); } } updateBG(); } fetchServerData(); console.log(currentBG) }, [city, units, currentBGref.current, currentFGref.current]) const isCelsius = currentUnit === "C"; button.innerText = isCelsius ? "°F" : "°C"; setUnits(isCelsius ? "metric" : "imperial"); }; return ( { dataset && ( )} ); } export default App; I can only pass the initial value and it never updates past that, although the console.log inside useEffect shows that it is definitely updating. So why isn't it being passed to my component?
useStateRefseems to be an anti-pattern. You decide what the new state is, so if you need another reference to it, you can always create one yourself. I recommend minimizing the properties on the canvas to prevent unnecessary re-rendering.function App({ width, height }) { const canvas = React.useRef() const [color, setColor] = React.useState("white") // when color changes.. React.useEffect(() => { if (canvas.current) { const context = canvas.current.getContext('2d'); context.fillStyle = color context.fillRect(0, 0, width, height) } }, [color, width, height]) return } ReactDOM.createRoot(document.querySelector("#app")).render( )canvas { display: block; border: 1px solid black; }Or maybe there's no reason to store colors as state. You can create the
setColorfunction yourself and attach it as an event listener -function App({ width, height }) { const canvas = React.useRef() const setColor = color => event => { if (canvas.current) { const context = canvas.current.getContext('2d'); context.fillStyle = color context.fillRect(0, 0, width, height) } } return } ReactDOM.createRoot(document.querySelector("#app")).render( )canvas { display: block; border: 1px solid black; }I also recommend checking out SVG. I find that the SVG API fits the React pattern better than the Canvas API. The capabilities of each are different, but if SVG works for your needs, it's worth considering.