问题描述
我正在尝试使用react recoil
进行状态管理,到目前为止我很喜欢它,但是目前我正尝试将其与react-hook-form
集成在一起,我可以找不到适合我的设置。
很简单,我想执行一个在完成onSubmit
之后上传文件Form
的函数。我已经设置了一个selector
来发出异步请求,并使用上传进度更新了一个相应的atom
,但是我在执行该函数时遇到了麻烦,因为它是在回调而不是函数中被调用的组件,我试图将其提取到自定义钩子中,但这也不起作用;我提供了以下代码,如果有人有任何想法请告诉我!
当前实现的示例错误:
React Hook "useRecoilValue" is called in function "submit" which is neither a React function component or a custom React Hook function
代码:
export const videoUploadProgressstate = atom({
key: "videoProgress",default: {progress: 0.0}
});
export const videoUploadState = selectorFamily({
key: 'videoUploadState',get: (data : {[key:string] : any}) => async ({get}) => {
const storage = get(firestoreStorageState);
const task = storage.ref(data.path).put(data.file);
const [progress,setProgress] = useRecoilState(videoUploadProgressstate)
task.on("state_changed",snapshot =>{
switch(snapshot.state){
case 'running':
setProgress({progress: snapshot.bytesTransferred / snapshot.bytesTransferred});
break;
}
}
);
const response = await fetch(
"http://*********:80/streaming/create-video-asset",{
method:'post',body:JSON.stringify({url: (await task).ref.getDownloadURL()})
});
console.log(response.json());
return response.json();
}
})
const UploadMovieComponent = () => {
const [title,setTitle] = useState("Select movie");
const {register,handleSubmit} = useForm();
const movies = useRecoilValue(movieState);
const progress = useRecoilValue(videoUploadProgressstate)
const submit = (data) => {
setTitle(
useRecoilValue(
videoUploadState(
{
path: `movies/${data.id}/${data.movieFile[0].name}`,file: data.movieFile[0]
}
)
)
);
}
return(
<div>
<Form onSubmit={handleSubmit(submit)}>
<Form.Group controlId="exampleForm.SelectCustom">
<Form.Label>Select movie</Form.Label>
<Form.Control as="select" ref={register} name="id" custom>
{movies.map(movie=> <option value={movie.id}>{movie.title}</option>)}
</Form.Control>
</Form.Group>
<Form.Group>
<Form.File name="movieFile" ref={register}/>
</Form.Group>
<Button type="submit" variant="outline-warning">Upload</Button>
</Form>
<ProgressBar className="mt-2" Now={progress.progress} />
</div>
);
}
解决方法
该解决方案涉及使用选择器上的set
属性传递一个data
对象,该对象包含我要发送给url
的参数。
通过这种方式,您可以使用useSetRecoilState
挂钩来获取可以在组件树范围之外使用的回调:
/**
* The components
*/
const CineburProgressBar = () => {
const [progress,setProgress] = useState(0.0);
const task = useRecoilValue(videoUploadProgressState);
task?.on("state_changed",(snapshot) => {
setProgress((snapshot.bytesTransferred / snapshot.totalBytes) * 100);
});
return (
<React.Suspense fallback={<div>Waiting...</div>}>
<ProgressBar className="mt-2" now={progress} />
</React.Suspense>
);
};
const UploadMovieComponent = () => {
const { register,handleSubmit } = useForm();
const [result,setResult] = useState("");
const movies = useRecoilValue(movieState);
const setRequestData = useSetRecoilState(videoUploadState);
return (
<div>
<Form
onSubmit={handleSubmit((data) =>
setRequestData({
id: data.id,file: data.movieFile[0],})
)}
>
<Form.Group controlId="exampleForm.SelectCustom">
<Form.Label>Select movie</Form.Label>
<Form.Control as="select" ref={register} name="id" custom>
{movies.map((movie) => (
<option value={movie.id}>{movie.title}</option>
))}
</Form.Control>
</Form.Group>
<Form.Group>
<Form.File name="movieFile" ref={register} />
</Form.Group>
<Button type="submit" variant="outline-warning">
Upload
</Button>
</Form>
<CineburProgressBar />
</div>
);
};
/**
* Firestore setup
*/
firebase.initializeApp(firebaseConfig);
export const firestoreState = atom({
key: "firestore",// unique ID (with respect to other atoms/selectors)
default: firebase.app().firestore(),// default value (aka initial value)
});
export const firestoreStorageState = atom({
key: "firestoreStorage",default: firebase.storage(),});
/**
* Recoil states
*/
export const movieState = selector({
key: "movieState",get: async ({ get }) => {
const firestore = get(firestoreState);
const movies = await firestore.collection("movies").get();
const movieNames = [...movies.docs].map((doc) => {
const movieData = doc.data();
return {
id: doc.id,title: movieData.title,assetRef: movieData.assetRef,};
});
return movieNames;
},});
/**
* Monitor video uploading
*/
export const videoUploadProgressState = atom<firebase.storage.UploadTask>({
key: "videoProgress",default: null,});
export const videoUploadState = selector({
key: "videoUploadState",get: async ({ get }) => {
return "";
},set: async ({ set,get },data: { [key: string]: any }) => {
const ref = `movies/${data.id}/video/${data.file.name}`;
const storage = get(firestoreStorageState);
const task = storage.ref(ref).put(data.file);
set(videoUploadProgressState,task);
const upload = await task;
const response = await fetch(
"http://*******:**/streaming/create-video-asset",{
method: "post",headers: {
"Content-type": "application/json",},body: JSON.stringify({ url: await upload.ref.getDownloadURL() }),}
);
const firestore = get(firestoreState);
const movieDoc = firestore.doc(`movies/${data.id}`);
const result = await response.json();
movieDoc.update({ assetRef: result.playback_id });
return result.playback_id;
},});