简体   繁体   中英

Using React hooks (useEffect and useRef) - Why ref gets updated on checkbox changes?

This is a model simulated from a real project.
Clicking undo button should change the state of checkboxes to the state before user changes them.
When I check/uncheck a checkbox, appRef.current gets updated? why?
the effect hook in App.jsx is only called once because its dependencies array is empty.

Confusion becomes more when you click check/uncheck all button then try to change the checkboxes and hit undo button but this time it works!!!


App.jsx

import { useEffect, useRef, useState } from 'react'
import Box from './box'

const App = () => {
    const [claims, setClaims] = useState()
    const appRef = useRef()

    const crudTrue = { Create: true, Read: true, Update: true, Delete: true }
    const crudFalse = { Create: false, Read: false, Update: false, Delete: false }
    const defaultClaims = {
        resource1: { ...crudFalse },
        resource2: { ...crudFalse, Read: true },
        resource3: { ...crudFalse, Read: true },
        resource4: { ...crudFalse }
    }

    useEffect(() => {
        // this effect should be called once
        // appRef should be set once
        appRef.current = { ...defaultClaims }
        setClaims({ ...defaultClaims })
    }, [])

    return (
        <Box
            claims={claims}
            // cloning to be extra sure it is not mutated
            boxRef={{ ...appRef }}
            onChange={setClaims}
            crudTrue={crudTrue}
            crudFalse={crudFalse}
        />
    )
}

export default App

box.jsx

import { createElement, useEffect, useRef } from 'react'
import CheckBox from './checkBox'

const Box = ({ claims, boxRef, onChange, crudTrue, crudFalse }) => {
    const checkRef = useRef()

    useEffect(() => (checkRef.current = boxRef.current), [boxRef])

    const handleChange = ({ currentTarget: checkbox }) => {
        const xclaims = { ...claims }
        const parts = checkbox.name.split('-')
        xclaims[parts[0]][parts[1]] = checkbox.checked
        onChange(xclaims)
    }

    const handleAll = () => {
        const xclaims = { ...claims }
        let isAnyClaimFalse = Object.keys(xclaims).some((res) =>
            Object.keys(xclaims[res]).some((c) => xclaims[res][c] === false)
        )
        for (let res in xclaims) xclaims[res] = isAnyClaimFalse ? { ...crudTrue } : { ...crudFalse }
        onChange(xclaims)
    }

    const handleUndo = () => onChange(checkRef.current)

    const renderChecks = () => {
        const children = []
        for (let res in claims)
            for (let key in claims[res])
                children.push(
                    <>
                        {key === 'Create' && res}
                        <CheckBox
                            key={res + '-' + key}
                            name={res + '-' + key}
                            label={key}
                            checked={claims[res][key]}
                            onChange={handleChange}
                        />
                        {key === 'Delete' && <br />}
                    </>
                )

        return createElement('div', [], ...children)
    }

    return (
        <>
            <button onClick={handleUndo}>undo</button>
            <button onClick={handleAll}>check/uncheck All</button>
            {renderChecks()}
        </>
    )
}

export default Box

checkbox.jsx

const CheckBox = ({ name, label, ...rest }) => (
    <>
        <input type='checkbox' id={name} name={name} {...rest} />
        {label && <label htmlFor={name}>{label}</label>}
    </>
)

export default CheckBox

Output is avilable here and codesandbox

Did not find the problem in your code, therefore I tried to solve it with my own solution. I changed some of the code completely. Maybe if you write back parts of it to yours you find out what was the root of the problem.

Here is the codesandbox link: https://codesandbox.io/s/using-react-hooks-useeffect-useref-1odx68

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM