如何在道具更改时更新状态,而无需渲染两次

How to update a state when a prop changes, without rendering twice

我有一个数据可视化组件,它以“宽度”作为道具之一。状态跟踪图表中的缩放级别,并在处理鼠标事件时更新。

当宽度改变时缩放需要调整。目前,我在 useEffect 挂钩中执行此操作:

function MyComponent({width}) {
    const [zoom, setZoom] = useState(...)
    
    useEffect(() => {
        setZoom(adjustZoom(zoom, width))
    }, [width])
    
    const handleMouseEvent = (event) => {
        setZoom(calculateNewZoom(event))
    }    
    
    ...
}

但这会使组件渲染两次:一次用于宽度更新,一次用于缩放更新。由于第一个渲染在屏幕上闪烁,因此并不理想。

理想情况下,组件只渲染一次,同时反映宽度和缩放的变化。如何用钩子实现这一点?另外,这个概念有名称吗?提前致谢。

Since the first render flashes on screen, it is not ideal.

这就是 useLayoutEffect() 旨在解决的问题,作为 useEffect() 的直接替代。

你还有另一个潜在的问题,那就是你的 useEffect() 包含 setZoom()stale reference to zoom. In order to obtain the correct reference, use the functional update 形式:

function MyComponent({ width }) {
    const [zoom, setZoom] = useState(...)

    useLayoutEffect(() => {
        setZoom((zoom) => adjustZoom(zoom, width))
    }, [width])

    const handleMouseEvent = (event) => {
        setZoom(calculateNewZoom(event))
    }
    ...
}

或者,您可以考虑删除 useLayoutEffect()using a memoized adjustedZoom 以避免双重呈现:

function MyComponent({ width }) {
    const [zoom, setZoom] = useState(...)

    const adjustedZoom = useMemo(() => {
        return adjustZoom(zoom, width)
    }, [zoom, width])

    const handleMouseEvent = (event) => {
        setZoom(calculateNewZoom(event))
    }
    ...
    // now use adjustedZoom where you would have used zoom before
}