使用 React 钩子(useEffect 和 useRef)——为什么 ref 在复选框更改时更新?

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

这是从真实项目中模拟出来的模型。
单击 撤消 按钮应该将复选框的状态更改为用户更改它们之前的状态。
当我 check/uncheck 一个复选框时,appRef.current 得到更新!为什么?
App.jsx 中的 effect 挂钩只被调用一次,因为它的 dependencies 数组是空的。

当你点击 check/uncheck all 按钮然后尝试更改复选框并点击 undo 按钮时,混乱变得更多,但这次它起作用了! !


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

检查box.jsx

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

export default CheckBox

输出可用 here and codesandbox

在你的代码中没有发现问题,因此我尝试用我自己的解决方案来解决它。我完全改变了一些代码。也许如果你把它的一部分写回你的,你就会发现问题的根源是什么。

这是代码框 link: https://codesandbox.io/s/using-react-hooks-useeffect-useref-1odx68