reactjs - 使用 react recoil 和 react-hook-form 执行获取请求
问题描述
我正在尝试使用 reactrecoil
进行状态管理,到目前为止我很喜欢它,但是目前我正在尝试将它与之集成,但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;
},
});
推荐阅读
- angular - 为什么在使用 --prod 编译时会从我的 Angular 库的服务中删除一个函数?
- java - Java 8 JVM 挂起,但在内存不足时不会崩溃/堆转储
- delphi - 当 ViewStyle 为 vsIcon 时如何使用 OnDrawItem 事件自定义绘制 ListView
- ios - Scrollview 中的两个动态 Stackview
- c - 在同一台机器上运行时,C 程序的浮点 [in] 精度在过去两周内发生了变化
- recursion - 卡在 MIPS 递归函数上
- css - laravel-mix-purgecss 和 Summernote
- c++ - 如何将指针变量作为引用参数传递?
- c# - 带有 Visual Studio 2017 WPF 的水晶报表
- git - .gitignore subdir/* 和 subdir/ 有什么区别?