【问题标题】:Issues with setting value with useState()使用 useState() 设置值的问题
【发布时间】:2021-12-10 14:54:50
【问题描述】:

我正在尝试使用 React Webcam 实现视频录制。录制视频后,应setSelectedFile。然后selectedFile 要么通过axios 发布方法上传到数据库,要么被另一个记录替换。每次录制后,用户应该可以查看最新的。

UI 按我的意愿工作,在录制过程中显示录制屏幕,在录制完成时显示录制文件。

我遇到的问题是我的selectedFile(需要通过 axios 发布)的状态值总是比它应该的值落后 1 个渲染。因此,在第一次录制后,selectedFile 未定义,在第二次录制后,设置为selectedFile 的文件实际上是第一次录制,依此类推。如果我刷新浏览器,整个过程会自行重置并重新开始循环。

我尝试从useEffect 调用setSelectedFile() 而没有useEffect (尽管如果我正确理解了useEffect,我认为我不应该在这里需要它,因为我不希望每个人都执行此操作使成为)。无论哪种方式,它似乎都不会对程序的行为产生任何影响。

setSelectedFile 调用我做错了什么吗?就像我说的那样,除了所选文件在它应该是 1 渲染(记录/停止记录)之外,一切工作正常。

我在下面附上我的代码。

const RecordNow = () => {


     const[details, setDetails] = useState({consent:false, 
        idConfirmed:false, 
        label:"", 
        roundId:""})

    const {
        startRecording,
        stopRecording,
        mediaBlobUrl,
      } = useReactMediaRecorder({ video: true, audio: true, blobPropertyBag: {
        type: "video/mp4"
    } });

      const [curStatus, setCurStatus] = useState(true);
      const [selectedFile, setSelectedFile] = useState([null]);
      const [isFilePicked, setIsFilePicked] = useState(false);

      useEffect(()=>{
          setSelectedFile();
      },[])

      // Handles upload of selected file via axios
      const uploadToDB =()=>{
          console.log("Called")
          setDetails(prevDetails=>({
            ...prevDetails,
            consent:true,
            idConfirmed:true,
            label:"test_Label"
        }));

      axios.post(process.env.REACT_APP, details)
                     .then(res=>{
                         console.log("Res:", res)
                        const data = new FormData();

                        data.append("file", selectedFile)
                        console.log("SelectedFile", selectedFile)

                        axios.post(`${process.env.REACT_APP}}/file/upload`, data,
                        {
                            headers:{
                              "Content-Type":"multipart/form-data"
                            }
                          })
                        .then(res=>{
                        console.log("Data: ",res.data)
                        console.log("success")
                })


                .catch((e)=>{
                    console.log("Error", e)
                })
                })
            }


        // Starts recording of new video
        const startedRec = () => {        
        startRecording();
        setIsFilePicked(false)
        setCurStatus(false);
    }


        // Stops recording of new video and creates blob
        const stoppedRec = async () => {
          stopRecording();
          const videoBlob = await fetch(mediaBlobUrl).then(r => r.blob());
          console.log("MediaBlob URL", mediaBlobUrl)
          const url = new Blob ([videoBlob]);

          //Creates a video file and filename
          const videoFile = new File([videoBlob], `wardround.${"mp4"}`, { type: "video/mp4" })

        console.log('Video File:', videoFile);
        console.log('Blob:', url);
            
        setSelectedFile(videoFile)
        console.log("Selected file", selectedFile)
        
        setCurStatus(true);
        setIsFilePicked(true)
        }

        // Handles input change and assigns input value to selectedFile variable
        const changeHandler = (event) => {
        setSelectedFile(event.target.files[0]);
          setIsFilePicked(true);
          setDetails(prevDetails=>({
            ...prevDetails,
            consent:true,
            idConfirmed:true,
            label:"test_Label"
        }));
        };
    

      return (
        <div>
          {!isFilePicked ?
          <div className='player-wrapper'>
              <Webcam audio={false} height={400} width={500}/>
          </div> :
          <video height="400" width="500" controls>
              <source src={mediaBlobUrl}/>
           </video>
          }
          {curStatus ?
          <span>     
              <button id="video-record" onClick={startedRec}>Record Video</button>
          </span>  
          :
          <span>     
              <button id="video-record" onClick={stoppedRec}>Stop Recording</button>
          </span>  
          }
          {!isFilePicked ?
          <span>
            <label 
              className="file-upload"
              htmlFor="file-upload">
                UPLOAD VIDEO
            </label>
            <input 
              id="file-upload"
              type="file"
              onChange={changeHandler} 
            /> 
          </span> :
          <span>
            <label 
              className="file-upload"
              htmlFor="confirm-upload">
                SUBMIT VIDEO
            </label>
            <input 
              id="confirm-upload"
              type="submit"
              onClick={() => uploadToDB()}
            /> 
          </span>}
        </div>
      );
    };

export default RecordNow

【问题讨论】:

  • 为 useEfect 提供正确的依赖关系,因此它会随着该状态的每次变化而更新
  • 每当您看到“1 个渲染落后于它应该是什么”时,请记住设置状态是异步的。例如。不能期望您的 setSelectedFile(videoFile); console.log("Selected file", selectedFile) 记录正确的值,因为该状态更改可能尚未发生。
  • @DBS。行。这就说得通了。您能否建议任何方法来允许在继续之前发生这种“状态”变化?
  • 在下面查看我对@SlothOverlord 回答的评论。

标签: reactjs use-state


【解决方案1】:

状态变化发生在 console.log 之后,所以你总是控制台 .log 之前的值。

解决方法是使用 useEffect 或 temp 变量

使用效果

      useEffect(()=>{
          console.log(selectedFile);
      },[selectedFile]) //<< only console.log once selectedFile is changed

温度值:

          //Creates a video file and filename
          const videoFile = new File([videoBlob], `wardround.${"mp4"}`, { type: "video/mp4" })

        console.log('Video File:', videoFile);
        console.log('Blob:', url);
            
        setSelectedFile(videoFile)
        //console.log("Selected file", selectedFile) <-- Wrong, stale data since setState is not finished jet.
        console.log("Selected file", videoFile) //<-- Use the data you are inserting into setSelectedFile to get to-be value.

【讨论】:

  • 所以,我想我已经在你的帮助下解决了这个问题。现在我可以看到 useEffect 中 console.log 中的 selectedFile 详细信息不再像我在 setSelectedFile(selectedFile) 之后立即登录控制台时那样是“未定义的”。但是,文件内容似乎仍然落后于“1”渲染。也就是说,在最初的“记录/停止”中,我可以看到一个没有实际记录的空 MP4 文件。这只能在第二次记录/停止之后访问。您可能有的任何建议都会很棒。谢谢
  • 当我进一步追踪上游时,我的stoppedRec 函数似乎也落后了。尽管它被称为异步/等待,console.log(videoBlob) 在第一次录制后是“text/html”类型,然后从第二次录制开始更改为“video/mp4”(如预期的那样)。 const stoppedRec = async () =&gt; { stopRecording(); const videoBlob = await fetch(mediaBlobUrl).then(r =&gt; r.blob()); console.log("VideoBLOB", videoBlob)
猜你喜欢
  • 1970-01-01
  • 2020-02-26
  • 2020-07-21
  • 2023-01-29
  • 2020-03-08
  • 2021-03-10
  • 2019-12-15
  • 1970-01-01
  • 2021-05-20
相关资源
最近更新 更多