#reactjs #use-state
#реагирует на #состояние использования
Вопрос:
Я пытаюсь реализовать запись видео с помощью веб-камеры React. Как только видео записано, оно должно setSelectedFile
быть записано . Затем selectedFile
он либо загружается в БД методом axios
post, либо заменяется другой записью. После каждой записи самая последняя должна быть доступна для просмотра пользователем.
Пользовательский интерфейс работает так, как я хочу, показывая экран перекодирования во время записи и записанный файл после завершения записи.
Моя проблема, с которой я сталкиваюсь, заключается в том, что значение состояния моего selectedFile
(необходимое для публикации через axios) всегда на 1 рендеринг меньше, чем должно быть. Таким образом, после первой записи selectedFile
значение не определено, а после второй записи файл устанавливается как selectedFile
на самом деле первая запись и так далее. Если я обновлю браузер, весь процесс сбросится и снова начнет цикл.
Я пытался звонить setSelectedFile()
из a useEffect
и без useEffect
(хотя, если я правильно понимаю эффект использования, я не думаю, что он мне здесь понадобится, так как я не хочу, чтобы это выполнялось при каждом рендеринге). В любом случае, похоже, это не имеет никакого значения для поведения программы.
Я что-то не так делаю с этим setSelectedFile
звонком. Как я уже сказал, все работает нормально, за исключением того, что выбранный файл на 1 рендеринг (запись/остановка записи) отстает от того, что должно быть.
Я прилагаю свой код ниже.
const RecordNow = () =gt; { 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(()=gt;{ setSelectedFile(); },[]) // Handles upload of selected file via axios const uploadToDB =()=gt;{ console.log("Called") setDetails(prevDetails=gt;({ ...prevDetails, consent:true, idConfirmed:true, label:"test_Label" })); axios.post(process.env.REACT_APP, details) .then(res=gt;{ 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=gt;{ console.log("Data: ",res.data) console.log("success") }) .catch((e)=gt;{ console.log("Error", e) }) }) } // Starts recording of new video const startedRec = () =gt; { startRecording(); setIsFilePicked(false) setCurStatus(false); } // Stops recording of new video and creates blob const stoppedRec = async () =gt; { stopRecording(); const videoBlob = await fetch(mediaBlobUrl).then(r =gt; 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) =gt; { setSelectedFile(event.target.files[0]); setIsFilePicked(true); setDetails(prevDetails=gt;({ ...prevDetails, consent:true, idConfirmed:true, label:"test_Label" })); }; return ( lt;divgt; {!isFilePicked ? lt;div className='player-wrapper'gt; lt;Webcam audio={false} height={400} width={500}/gt; lt;/divgt; : lt;video height="400" width="500" controlsgt; lt;source src={mediaBlobUrl}/gt; lt;/videogt; } {curStatus ? lt;spangt; lt;button id="video-record" onClick={startedRec}gt;Record Videolt;/buttongt; lt;/spangt; : lt;spangt; lt;button id="video-record" onClick={stoppedRec}gt;Stop Recordinglt;/buttongt; lt;/spangt; } {!isFilePicked ? lt;spangt; lt;label className="file-upload" htmlFor="file-upload"gt; UPLOAD VIDEO lt;/labelgt; lt;input id="file-upload" type="file" onChange={changeHandler} /gt; lt;/spangt; : lt;spangt; lt;label className="file-upload" htmlFor="confirm-upload"gt; SUBMIT VIDEO lt;/labelgt; lt;input id="confirm-upload" type="submit" onClick={() =gt; uploadToDB()} /gt; lt;/spangt;} lt;/divgt; ); }; export default RecordNow
Комментарии:
1. дайте правильную зависимость эффекту использования, чтобы он обновлялся при каждом изменении этого состояния
2. Всякий раз, когда вы видите «1 визуализация за тем, что должно быть», помните, что состояние настройки асинхронно. Например
setSelectedFile(videoFile); console.log("Selected file", selectedFile)
, нельзя ожидать, что вы зарегистрируете правильное значение, потому что это изменение состояния, вероятно, еще не произошло.3. @DBS. Хорошо. В этом есть смысл. Не могли бы вы предложить какой-либо способ разрешить это изменение «состояния», прежде чем продолжить?
4. Смотрите мой комментарий к ответу @SlothOverlord ниже.
Ответ №1:
Изменение состояния происходит после console.log, поэтому вы всегда выполняете консоль .запишите в журнал предыдущее значение.
Решение состоит в том, чтобы использовать переменные useEffect или temp
Эффект использования
useEffect(()=gt;{ console.log(selectedFile); },[selectedFile]) //lt;lt; 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) lt;-- Wrong, stale data since setState is not finished jet. console.log("Selected file", videoFile) //lt;-- Use the data you are inserting into setSelectedFile to get to-be value.
Комментарии:
1. Итак, я думал, что решил эту проблему с вашей помощью. Теперь я вижу, что выбранные данные файла из консоли. вход в useEffect больше не является «неопределенным», как это было, когда я вошел в консоль сразу после setSelectedFile(Выбранный файл). Однако содержимое файла по-прежнему выглядит как «1», отображаемое сзади. То есть при начальной «записи/остановке» я вижу пустой файл MP4, в котором нет фактической записи. Это доступно только после второй записи/остановки». Любые предложения, которые у вас могут быть, были бы замечательными. Спасибо
2. Когда я отслеживаю дальнейшее движение вверх по течению, моя функция stoppedRec также, кажется, отстает. Несмотря на то, что он вызывается как асинхронный/подождите, console.log(видеоБлоб) имеет тип «текст/html» после первой записи, а затем изменяется на «видео/mp4» (как и ожидалось) со второй записи и далее.
const stoppedRec = async () =gt; { stopRecording(); const videoBlob = await fetch(mediaBlobUrl).then(r =gt; r.blob()); console.log("VideoBLOB", videoBlob)