I stumbled upon a very interesting question and I would like to know how to best solve this in React. Assume the following code:
const [qrText, setQrText] = useState("")
...
const generateQrCode = () => {
// set other state inside the "then"
QRCode.toDataUrl(qrText).then(...)
}
const handleChange = (e) => {
setQrText(e.target.value)
generateQrCode()
}
This code is unsafe, since state updates are asynchronously, and by the time generateQrCode runs, qrText could still have the old value.
I always tended to solve this problem using a useEffect with dependency array:
const [qrText, setQrText] = useState("")
...
const handleChange = (e) => {
setQrText(e.target.value)
}
useEffect(() => {
const generateQrCode = () => {
// set other state inside the "then"
QRCode.toDataUrl(qrText).then(...)
}
generateQrCode()
}, [qrText])
However, I recently watched a YouTube video from a React conference, where a senior engineer said that useEffect is only supposed to be used to synchronize data with external services or the DOM. Instead, people should update state in event handlers only.
So is this the right way then to handle this scenario?
const [qrText, setQrText] = useState("")
...
// this now takes the qrText as argument
const generateQrCode = (qrTextArg) => {
// set other state inside the "then"
QRCode.toDataUrl(qrTextArg).then(...)
}
const handleChange = (e) => {
const value = e.target.value
setQrText(value)
generateQrCode(value) // pass the event value, instead of relying on the "qrText" state
}
This would equal the "event based" approached, but feels a bit imperative and not "react"-ish.
So I wonder, what is the intended way to do this?
Thanks for your answers!