You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React自定义hook回调仅能访问初始state问题求解

问题根因

这是典型的React Hooks闭包陷阱:

  • 首次渲染时创建的ensureMounted和play函数,捕获了初始的state值(mounted: false)
  • 后续mounted状态更新触发hook重渲染时,虽然会生成新的函数版本,但只要调用方持有的是旧版本的play函数引用,就永远只能读到初始化时的state,无法拿到最新值。
解决方案

方案1:用Ref存最新状态(推荐,适配状态频繁更新场景)

Ref的current属性永远指向最新值,不受闭包限制,不用依赖函数引用更新就能读到最新状态:

const usePlayer = () => {
    const audioRef = useRef(__GLOBAL_AUDIO);
    const [state, setState] = useReducer((state, newState) => ({...state, ...newState}), {
        mounted: false,
        playing: false,
    });
    // 新增ref同步最新state
    const latestStateRef = useRef(state);
    // state变化时自动同步到ref
    useEffect(() => {
        latestStateRef.current = state;
    }, [state]);

    const ensureMounted = callback => {
        // 从ref读取最新状态,不会被闭包卡住
        if(latestStateRef.current.mounted){
            callback();
            return;
        }
        audioRef.current.src = cdn("/link/to/new/src.mp3");
        onLoad(callback);
    };

    const play = () => ensureMounted(() => { 
        audioRef.current.play(); 
    });

    useEffect(() => {
        const _mounted = checkToSeeIfMounted();
        setState({ mounted: _mounted });
    }, deps);

    return {
        playing: state.playing,
        play: play,
    };
};

方案2:用useCallback补全依赖

如果调用方可以正确接收更新后的函数引用,可以用useCallback显式声明依赖,每次状态变化后返回新的函数版本:

// ensureMounted依赖state,state变化时生成新函数
const ensureMounted = useCallback(callback => {
    if(state.mounted){
        callback();
        return;
    }
    audioRef.current.src = cdn("/link/to/new/src.mp3");
    onLoad(callback);
}, [state]);

// play依赖ensureMounted,同步更新
const play = useCallback(() => ensureMounted(() => { 
    audioRef.current.play(); 
}), [ensureMounted]);

注意使用这个方案时,调用方如果用到useCallback、useMemo或者React.memo,必须把play加入依赖数组,否则还是会持有旧的函数引用。

内容的提问来源于stack exchange,提问作者Grover

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.05 20:45:03