使用react recoil和react-hook-form执行获取请求

问题描述

我正在尝试使用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;
  },});


相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...