You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在React组件中使用自定义useStorage钩子返回的url值

实现方案

步骤1:先完善useStorage的内部状态声明

你提供的useStorage缺失了内部状态的定义,同时有闭包问题和边界判断缺失,先修正为可正常运行的版本:

import { useState, useEffect } from 'react';
import { ref, uploadBytesResumable, getDownloadURL } from "firebase/storage";
import { firebaseStorage, timestamp } from '你的firebase配置文件路径'; // 替换为自己项目的配置引入

export const useStorage = (file: File | null) => {
  // 补上缺失的内部状态定义
  const [progress, setProgress] = useState<number>(0);
  const [error, setError] = useState<string | null>(null);
  const [url, setUrl] = useState<string | null>(null);
  const [createdAt, setCreatedAt] = useState<Date | null>(null);

  useEffect(() => {
    // 文件为空时不执行上传逻辑
    if (!file) return;
    const storageRef = ref(firebaseStorage, `upload/${file.name}`);
    const uploadTask = uploadBytesResumable(storageRef, file);

    uploadTask.on(
      "state_changed",
      (snapshot) => {
        const percentage = (snapshot.bytesTransferred / snapshot.totalBytes) * 100;
        setProgress(percentage);
        console.log("Upload is " + percentage + "% done"); // 修复原代码的闭包问题,直接用当前计算的进度
      },
      (error) => {
        setError(error.message);
      },
      () => {
        getDownloadURL(uploadTask.snapshot.ref).then((downloadURL) => {
          console.log("File available at ", downloadURL);
          setUrl(downloadURL);
          setCreatedAt(timestamp());
        });
      }
    );
    // 组件卸载时取消上传,避免内存泄漏
    return () => uploadTask.cancel();
  }, [file]);

  return { url, createdAt, error, progress };
};

步骤2:在表单组件中同步url到表单状态

核心逻辑是调用useStorage拿到返回的url后,通过useEffect监听url变化,自动更新到表单状态中:

import { useState, useEffect } from 'react';
import { useStorage } from '你存放useStorage的文件路径';

// 定义表单类型
type FormState = {
  drinkName: string;
  description: string;
  category: string;
  price: string;
  file: string | null; // 这里改为存储上传后的url
}

const 你的表单组件 = () => {
  const [formState, setFormState] = useState<FormState>({
    drinkName: "",
    description: "",
    category: "",
    price: "",
    file: null,
  });
  // 单独存储用户选择的原始文件,传给useStorage触发上传
  const [selectedFile, setSelectedFile] = useState<File | null>(null);
  // 调用自定义hook获取上传结果
  const { url, progress, error } = useStorage(selectedFile);

  // 监听url变化,上传完成后自动同步到表单状态
  useEffect(() => {
    if (url) {
      setFormState(prev => ({
        ...prev,
        file: url
      }));
    }
  }, [url]);

  // 绑定到文件选择框的onChange事件
  const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    if (e.target.files?.[0]) {
      setSelectedFile(e.target.files[0]);
    }
  };

  const handleSubmit = (e: React.FormEvent) => {
    e.preventDefault();
    // 提交前做校验,避免未完成上传就提交
    if (selectedFile && !formState.file) {
      alert('文件正在上传中,请稍候再提交');
      return;
    }
    if (error) {
      alert(`文件上传失败:${error}`);
      return;
    }
    // 此时formState.file已经是上传完成的url,直接提交即可
    console.log(formState);
    // 后续执行你自己的表单提交逻辑
  };

  return (
    <form onSubmit={handleSubmit}>
      {/* 其他表单字段省略 */}
      <input type="file" onChange={handleFileChange} accept="image/*" />
      {/* 可选择性展示上传进度,提升用户体验 */}
      {progress > 0 && progress < 100 && <p>上传进度:{progress.toFixed(2)}%</p>}
      <button type="submit">提交</button>
    </form>
  )
}

可选优化

如果不需要选完文件就自动上传,也可以把上传逻辑移到handleSubmit中,提交时再上传文件、拿到url后再提交整个表单,避免用户选了文件又取消产生无效的存储占用。

内容的提问来源于stack exchange,提问作者AbdulAzeez Olanrewaju

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.29 11:24:00