React 上下文状态更新总是落后一步

时间:2021-03-06 00:47:55

标签: javascript reactjs

我读过类似标题的问题,但它们没有解决我的问题。

我有一个 API 调用,其结果需要在多个组件之间共享。父组件进行调用,React 的上下文用于在子组件之间共享它:

MainPage.js:

import React, { useState, useEffect } from "react";
import { useParams } from "react-router-dom";
import { VideoPlayer } from "../components/VideoPlayer";
import VideoContext from "../components/VideoContext";


export default function Watch() {
    const [video, setVideo] = useState({});
    const { videoHash } = useParams();
    const [isLoading, setIsLoading] = useState(true);

    useEffect(() => {
        setIsLoading(true);
        getVideo();
    }, [videoHash]);

    const getVideo = async () => {
        if(videoHash) {
            const res = await getVideoFromApi();
            setIsLoading(false);
            // The first time this runs nothing happens, the video can't be played
            // The second time it runs (i.e. when the URL/videoHash changes) it updates 
            // but it shows the video from the first run
            setVideo(res.video);
        }
    };

    return (
        <>
            {isLoading ? (
                <div>Loading...</div>
            ) : (
                <VideoContext.Provider value={{ video, setVideo }}>
                    <VideoPlayer videoHash={videoHash} />
                </VideoContext.Provider>
            )}
        </>
    );
}

VideoPlayer.js:

import React, { useState, useEffect, useContext } from "react";
import VideoContext from "./VideoContext";
import styles from "./VideoPlayer.module.css";

export function VideoPlayer({ videoHash }) {
    const { video, setVideo } = useContext(VideoContext);
    const [forceRender, setforceRender] = useState(true);

    useEffect(() => {
        // I tried adding this to no effect
        setforceRender(!forceRender);
    }, [videoHash]);

    return (
        <video controls className={styles["video-player"]}>
            <source src={video.VideoUrl} type="video/mp4" />
                Sorry, your browser does not support embedded videos.
        </video>
    );
}

VideoContext.js

import { createContext } from "react";

export default createContext({
    video: {}, 
    setVideo: () => {}
});

它在页面加载时工作,但是当我的 Link 组件更改 videoHash 属性时,新视频会加载(我可以在 console.log() API 调用时看到)但它没有在视频播放器中更新。

第二次点击链接并更改 videoHash 参数时,视频会显示,但它是针对上一个视频的。

1 个答案:

答案 0 :(得分:1)

https://codesandbox.io/s/blazing-lake-k4i8n?file=/src/VideoPlayer.js

除非我遗漏了一些东西,否则我认为 VideoPlayer 只是作为一个没有任何状态钩子的功能组件可以正常工作,可以由 Watch 处理。当您单击指向将指向观看的另一条路线的链接时,videoHash 将发生变化

VideoPlayer.js

import React from "react";
import { Link } from "react-router-dom";

export function VideoPlayer({ videoHash }) {
  // const { video, setVideo } = useContext(VideoContext);
  // const [forceRender, setforceRender] = useState(true);

  // useEffect(() => {
  //   // I tried adding this to no effect
  //   setforceRender(!forceRender);
  // }, [videoHash]);
  // console.log(video);
  // Am I missi
  return (
    <div>
      Am I missing something or could you just use your videoHash: {videoHash},
      here?
      <Link to="/watch/a">Previous</Link>
      <Link to="/watch/c">Next</Link>
    </div>
  );
}

Watch.js

import React, { useState, useEffect, useCallback } from "react";
import { useParams } from "react-router-dom";
import { VideoPlayer } from "./VideoPlayer";
import VideoContext from "./VideoContext";

export default function Watch() {
  const [video, setVideo] = useState({});
  const { videoHash } = useParams();
  const [isLoading, setIsLoading] = useState(true);

  const getVideo = useCallback(async () => {
    if (videoHash) {
      const res = await getVideoFromApi();
      setTimeout(() => {
        setIsLoading(false);
        setVideo(res);
      }, 1000);
    }
  }, [videoHash]);

  useEffect(() => {
    setIsLoading(true);
    getVideo();
  }, [getVideo]);

  const getVideoFromApi = async () => {
    const videoArray = ["A", "B", "C"];
    const randomItem =
      videoArray[Math.floor(Math.random() * videoArray.length)];
    return Promise.resolve(randomItem);
  };

  return (
    <>
      {isLoading ? (
        <div>Loading...</div>
      ) : (
        <VideoContext.Provider value={{ video, setVideo }}>
          <VideoPlayer videoHash={videoHash} />
        </VideoContext.Provider>
      )}
    </>
  );
}

VideoContext.js

import { createContext } from "react";

export default createContext({
  video: "",
  setVideo: () => {}
});

我添加了一个超时,以便您可以看到加载部分也能正常工作。如果我遗漏了您需要做的事情,请告诉我。