如何使用自定义钩子>

时间:2021-04-23 07:07:30

标签: reactjs react-hooks

我正在将该图像的图像和标题上传到 firebase。我制作了一个自定义钩子(useStorage),用于上传该图像的图像和标题。我有两个单独的组件 UploadForm 和 ProgressBar,我将标题和选定的图像和标题从 UploadForm 传递到 ProgressBar。我可以在 firestore 中为图像和标题创建字段,图像正在上传但标题字段仍为空字符串。

我正在附上 3 个文件的代码,任何帮助将不胜感激。

import { useState, useEffect } from 'react';
import { projectStorage, projectFirestore, timestamp } from '../firebase/config';

const useStorage = (file) => {
  const [progress, setProgress] = useState(0);
  const [error, setError] = useState(null);
  const [url, setUrl] = useState(null);
  const [title, setTitle] = useState('');

  useEffect(() => {
    // references
    const storageRef = projectStorage.ref(file.name);
    const collectionRef = projectFirestore.collection('images');
    
    storageRef.put(file).on('state_changed', (snap) => {
      let percentage = (snap.bytesTransferred / snap.totalBytes) * 100;
      setProgress(percentage);
    }, (err) => {
      setError(err);
    }, async () => {
      const url = await storageRef.getDownloadURL();
      const createdAt = timestamp();
      await collectionRef.add({ url, createdAt, title });
      setUrl(url);
      setTitle(title);
    });
  }, [file, title]);

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

export default useStorage;

import React, { useState } from 'react';
import ProgressBar from './ProgressBar';

const UploadForm = () => {
  const [file, setFile] = useState(null);
  const [error, setError] = useState(null);
  const [header, setHeader] = useState('')

  const types = ['image/png', 'image/jpeg'];

  const handleChange = (e) => {
    let selected = e.target.files[0];

    if (selected && types.includes(selected.type)) {
      setFile(selected);
      setError('');
    } else {
      setFile(null);
      setError('Please select an image file (png or jpg)');
    }
  };

  return (
    <form>
      <label>
        <input type="file" onChange={handleChange} />
        <span>+</span>
      </label>
      
      <input placeholder="Enter title for pic..." type="text" name={header} onChange={(e)=> setHeader(e.target.value)} />
      
      <div className="output">
        { error && <div className="error">{ error }</div>}
        { file && <div>{ file.name }</div> }
        { file && <ProgressBar file={file} setFile={setFile} header={header} /> }
      </div>
    </form>
  );
}

export default UploadForm;

import React, { useEffect } from 'react';
import useStorage from '../hooks/useStorage';
import { motion } from 'framer-motion';

const ProgressBar = ({ file, setFile, header }) => {
  const { progress, url, title } = useStorage(file);

  useEffect(() => {
    if (url) {
      setFile(null);
    } 
  }, [url, setFile]);

  return (
    <motion.div className="progress-bar"
      initial={{ width: 0 }}
      animate={{ width: progress + '%' }}
    ></motion.div>
  );
} 

export default ProgressBar;

2 个答案:

答案 0 :(得分:1)

useStorage 有一个 [title, setTitle] 钩子用于

   await collectionRef.add({ url, createdAt, title });
   setUrl(url);
   setTitle(title);

但问题是标题是空的。你从不设置标题。您将 header 传递给进度条,但您不使用它。您应该更新 useStorage 的两个参数:file 和 header 并调用 useStorage(file, header)

将 useStorage 签名更新为 const useStorage = (file, header) => {} 在 useStorage 中,您可以更新 const [title, setTitle] = useState(header)

答案 1 :(得分:0)

据我所知,您永远不会从任何地方获得 title。您所做的就是将使用 title 初始化的状态变量 '' 发送到 Firestore,然后将 setTitle 发送到相同的空字符串 title

您的意思是将 headerfile 一起传递到您的钩子中吗?