Unable to set previousCode state using useState hook in React

Viewed 44

I am new to react and javascript and cannot understand why setPreviousCode is not changing the state. I want to store the previous scanned text in previousCode.

import React, { useEffect, useState } from "react"
import { Html5Qrcode } from "html5-qrcode"
import { useCallback } from "react"

let html5QrCode
const brConfig = {
    fps: 10,
    qrbox: { width: 300, height: 150 },
    disableFlip: false,
}

export const Scanner = () => {
    const beep = new Audio("https://www.soundjay.com/buttons/beep-08b.mp3")

    const [previousCode, setPreviousCode] = useState(null)

    useEffect(() => {
        html5QrCode = new Html5Qrcode("reader")
        startScan()
    }, [])

    const startScan = useCallback(() => {
        console.log("imrunngin")
        const qrCodeSuccessCallback = (decodedText, decodedResult) => {
            if (previousCode !== decodedText) {
                beep.play()
                setPreviousCode(decodedText)
            }
        }
        html5QrCode.start(
            { facingMode: "environment" },
            brConfig,
            qrCodeSuccessCallback
        )
    }, [])

    const handleStop = () => {
        try {
            html5QrCode
                .stop()
                .then((res) => {
                    html5QrCode.clear()
                    console.log("stopped")
                })
                .catch((err) => {
                    console.log(err.message)
                })
        } catch (err) {
            console.log(err)
        }
    }

    return (
        <div style={{ position: "relative", backgroundColor: "#1E1E1E" }}>
            <div id="reader" width="100%" />
        </div>
    )
}

1 Answers

When you update a state property, with respect to its previous value, use the callback argument* of the state setter. Otherwise stale state values may be taken into account.

https://reactjs.org/docs/state-and-lifecycle.html#state-updates-may-be-asynchronous

Try with,

 const qrCodeSuccessCallback = (decodedText, decodedResult) => {
     setPreviousCode((previousCode)=> {
         if (previousCode !== decodedText) {
            beep.play()
            return decodedText;
         }
         return previousCode
     });
 }

Or even better (the state setter is pure):

const qrCodeSuccessCallback = (decodedText, decodedResult) => {
     setPreviousCode((previousCode)=> (previousCode !== decodedText) ?decodedText:previousCode
     });
 }

 useEffect(()=>{
     beep.play();
 }, [previousCode]);
Related