从自定义挂钩返回对象时防止重新渲染

问题描述 投票:0回答:1

我希望这个钩子仅在实际结果发生变化时(即 isLoading、isPlaying、isPaused)触发任何订阅组件中的重新渲染:

export const usePlaybackState = (): {
    isLoading: boolean
    isPlaying: boolean
    isPaused: boolean
} => {
    const { state: stateFromLib } = usePlaybackStateLib()

    const stateRef = useRef({
        isLoading: false,
        isPlaying: false,
        isPaused: false,
    })

    const newState = mapInternalPlayerStateToPlaybackProps(stateFromLib, stateRef.current)

    const memoizedState = useMemo(() => {
        if (
            newState.isLoading !== stateRef.current.isLoading ||
            newState.isPlaying !== stateRef.current.isPlaying ||
            newState.isPaused !== stateRef.current.isPaused
        ) {
            console.log('>>>State changed:', newState)
            stateRef.current = newState
        }
        return stateRef.current
    }, [newState.isLoading, newState.isPlaying, newState.isPaused])
    console.log('>>>memoizedState', memoizedState, stateFromLib)

    return memoizedState
}


export const [PlaybackStateProvider, usePlaybackStateContext] = constate(usePlaybackState)

但是…stateFromLib 非常嘈杂,每次它发生变化时,即使结果根本没有变化,我也会收到大量的重新渲染。就这样(反复)

>>>memoizedState {"isLoading": true, "isPaused": false, "isPlaying": false} buffering
>>>memoizedState {"isLoading": true, "isPaused": false, "isPlaying": false} ready
>>>isPlaying false false true
>>>memoizedState {"isLoading": true, "isPaused": false, "isPlaying": false} buffering
>>>memoizedState {"isLoading": true, "isPaused": false, "isPlaying": false} ready
>>>isPlaying false false true

知道为什么吗?感觉一定是因为我正在返回一个对象,但尽管记忆应该处理这个问题

reactjs react-hooks
1个回答
0
投票

自定义挂钩可帮助您重用逻辑。它们防止复制/粘贴。他们没什么特别的。 与在组件主体中调用整个代码相同

如果自定义挂钩中的某些内容导致重新渲染,则组件将重新渲染。你返回什么并不重要

但是有几种方法可以用来达到类似的效果。

const PlaybackContext = React.createContext<your_value_type_here>()

const PlaybackProvider = ({children}: {children: React.ReactNode}) => {
    const { state: stateFromLib } = usePlaybackStateLib()

    const newState = mapInternalPlayerStateToPlaybackProps(stateFromLib, stateRef.current)

    const value = useMemo(() => {
        return {
           loading: newState.loading,
           isPlaying: newState.isPlaying, 
           isPaused: newState.isPaused
        }
    }, [newState.isLoading, newState.isPlaying, newState.isPaused])
    
    return <PlaybackContext.Provider value={value}>{children}</PlaybackContext.Provider>
}

const usePlaybackContext = () => React.useContext(PlaybackContext)

现在

PlaybackProvider
每次都会重新渲染,但是使用其值的组件仅在其值发生变化时才会重新渲染。仅当这 3 种状态中至少有一种发生变化时,它才会发生变化。

用途:

<PlaybackProvider><MyComponent/><AnotherComponent/>...</PlaybackProvider>

里面

MyComponent
:

const playbackState = usePlaybackContext()

PlaybackProvider
应该包装所有使用公共状态的内容!

© www.soinside.com 2019 - 2024. All rights reserved.