使用自定义钩子不触发 useState 更新

时间:2021-05-30 13:08:19

标签: reactjs

我像这样为我的播放器制作了一个自定义钩子

const usePlayer = () => {
  const [volume, setVolume] = useState<number>(100);
  const playerRef = useRef<HTMLAudioElement | null>(null);
  const [playing, setPlaying] = useState<boolean>(false);
  useEffect(() => {
    console.log(volume);
  }, [volume]);
  return {
    playing,
    volume,
    setPlaying,
    setVolume,
    playerRef,
  };
};
export default usePlayer;

我正在监听另一个使用 userPlayer() 的组件的变化

const PlayerButton = () => {
  const { playerRef, volume } = usePlayer();

  useEffect(() => {
    console.log(volume);
    if (playerRef.current !== null) {
      playerRef.current.volume = volume / 100;
    }
  }, [volume]);
  return (
    <>
      <div className="lg:mr-2 flex align-middle">
        <audio ref={playerRef} preload="none">
        <source src="" type="audio/ogg"></source>
        <source src="" type="audio/mp3"></source>
      </audio>
      </div>
    </>
  );
};

export default PlayerButton

setVolume 在另一个组件中是这样调用的

  <input
     type="range"
     className="w-full"
     value={volume}
     onChange={(e) => setVolume(parseInt(e.target.value))}
     min={0}
     max={100}
         />

但是 useEffect 只会在 usePlayer 组件内部触发,而不会在使用 usePlayer() 的第二个组件中触发

知道为什么吗?

1 个答案:

答案 0 :(得分:1)

如果您在 PlayerButton 组件中使用 setVolume,它将触发 PlayerButton 中的 useEffect。

如果您在不同的组件中使用 setVolume,它不会触发 useEffect。

<块引用>

使用相同 Hook 的两个组件是否共享状态? 否。自定义 Hook 是一种重用有状态逻辑的机制(例如设置一个 订阅并记住当前值),但每次 使用自定义 Hook,其中的所有状态和效果都完全 隔离。

自定义 Hook 如何获得隔离状态? 每次调用 Hook 都会获得 孤立状态。因为我们直接调用 useFriendStatus,从 React 的 从角度来看,我们的组件只是调用 useState 和 useEffect。并作为 我们之前学过,我们可以多次调用 useState 和 useEffect 一个组件,它们将完全独立。

https://reactjs.org/docs/hooks-custom.html

如果你想让 2 个组件共享 state,你需要使用 redux 或者 useContext 来管理 store 中的 state。

https://reactjs.org/docs/hooks-reference.html#usecontext

useContext 和 Provider 的使用示例。

export const PlayerContext = createContext();
export const usePlayer = () => {
    const context = useContext(PlayerContext);
    if (!context && typeof window !== 'undefined') {
        throw new Error(`usePlayer must be used within a PlayerContext `);
    }
    return context;
};

export const PlayerProvider = ({ children }) => {

    const [ volume, setVolume ] = useState(100);

    return <PlayerContext.Provider value={{ volume, setVolume }}>{children}</PlayerContext.Provider>
}

您需要像这样使用提供程序包装您的应用。

<PlayerProvider><App /></PlayerProvider>

然后像这样使用

const { volume, setVolume } = usePlayer()