点击提交按钮后上传图片,而不是输入更改

问题描述 投票:0回答:4

我想创建一个表单,将图片上传到 Cloudinary,然后获取图片的 URL 并将其解析到 Supabase 数据库中,但我只想在单击 Publish 按钮时上传图片(现在图片将自动上传当我选择它时)。

所以我想知道如何为此目的创建一个函数。请帮忙!谢谢。

这是我的代码:

'use client';

import { useState, useEffect } from 'react'
import { useRouter, redirect } from 'next/navigation';
import { useUser } from '@/contexts/AuthContext';
import { Database } from '@/db_types'
import Button from "@/components/shared/Button"
import Editor from "@/components/shared/Editor"

type Posts = Database['public']['Tables']['posts']['Row']

const initialState = {
  title: "",
  description: "",
  slug: "",
  images: "",
};

export default function AddPost() {

  const { userDetails , supabase } = useUser();
  const [loading, setLoading] = useState(true)
  const [title, setTitle] = useState<Posts['title']>(null)
  const [description, setDescription] = useState<Posts['description']>(null)
  const [slug, setSlug] = useState<Posts['slug']>(null)
  const [user_id] = useState<Posts['user_id']>(null)
  const [images, setImages] = useState<Posts['images']>(null)
  const [imageData, setImageData] = useState(initialState);


  if (!userDetails) {
    redirect('/login');
  }


  const uploadImage = async (e : any) => {
    const reader = new FileReader();
    reader.onloadend = async () => {
      setLoading(true);
    };
    if (!e.target.files || e.target.files.length === 0) {
      throw new Error('You must select an image to upload.')
    }

    const files = e.target.files[0]
    if (!files) return;
    const data = new FormData();
    data.append("file", files);
    data.append("upload_preset", "c_tags");
    const res = await fetch(`${process.env.NEXT_PUBLIC_CLOUDINARY_API}`,
      {
        method: "POST",
        body: data,
      }
    );
    const file = await res.json();
    setImageData({ ...imageData, images: file.secure_url });
    setLoading(false);
  };

  async function addPost({
    title,
    description,
    slug,
    images,
    user_id,
  }: {
    title: Posts['title']
    description: Posts['description']
    slug: Posts['slug']
    images: Posts['images']
    user_id: Posts['user_id'] 
  }) {
    try {
        setLoading(true)
        

        
        const updates = {
          title,
          description,
          slug,
          images : `${imageData.images}`,
          created_at: new Date().toISOString(),
          user_id: userDetails?.id
        }

        let { error } = await supabase.from('posts').upsert(updates)
        if (error) throw error
        alert('Published!')
        } catch (error) {
        alert('Error updating the data!')
        console.log(error)
        } finally {
        setLoading(false)
        }
    }
   
  return (
    <div className="">
      <div>
      <form
          className="mt-3"
          onSubmit={(e) => {
            e.preventDefault();
            addPost({ title, description, slug, images, user_id })
          }}
        >
        <input
          id="images"
          type="file"
          className="relative block w-full appearance-none rounded-none rounded-md border border-gray-300 px-3 py-2 text-gray-900 placeholder-gray-500 focus:z-10 focus:border-indigo-500 focus:outline-none focus:ring-indigo-500 sm:text-sm"
          accept="image/*"
          onChange={uploadImage}
        />

        <label htmlFor="title">Title</label>
        <input
          id="title"
          type="text"
          className="relative block w-full appearance-none rounded-none rounded-md border border-gray-300 px-3 py-2 text-gray-900 placeholder-gray-500 focus:z-10 focus:border-indigo-500 focus:outline-none focus:ring-indigo-500 sm:text-sm"
          value={title || ''}
          onChange={(e) => setTitle(e.target.value)}
        />
   
        
        <label htmlFor="Description">Content</label>
        <Editor
            description={description}
            setDescription={setDescription}
        />
    
        <label htmlFor="slug">Slug</label>
        <input
          id="slug"
          type="text"
          className="relative block w-full appearance-none rounded-none rounded-md border border-gray-300 px-3 py-2 text-gray-900 placeholder-gray-500 focus:z-10 focus:border-indigo-500 focus:outline-none focus:ring-indigo-500 sm:text-sm"
          value={slug || ''}
          onChange={(e) => setSlug(e.target.value)}
        />
          <div>
          <Button
            className="mt-5 bg-red-500"
            onClick={() => addPost({ title, description, slug, user_id, images })}
          >
            Publish
          </Button>
          </div>
        </form>
      </div>
    </div>
  );
}
javascript reactjs next.js cloudinary supabase
4个回答
0
投票

onClick
中删除
Button
方法并声明它的类型
submit

<Button className="mt-5 bg-red-500" type="submit">
    Publish
</Button>

0
投票

您可以将图片上传逻辑从 uploadImage 函数移动到 addPost 函数。

  • 从图像输入字段中删除 onChange 事件以防止自动上传。
  • 修改uploadImage函数只读取选中的文件并存入state
  • 将实际的图片上传逻辑移至 addPost 函数中。

// ...

const [selectedFile, setSelectedFile] = useState<File | null>(null);

const onImageSelect = (e: any) => {
  if (!e.target.files || e.target.files.length === 0) {
    throw new Error('You must select an image to upload.');
  }
  const files = e.target.files[0];
  if (!files) return;
  setSelectedFile(files);
};

async function uploadImage() {
  if (!selectedFile) return;
  const data = new FormData();
  data.append('file', selectedFile);
  data.append('upload_preset', 'c_tags');
  const res = await fetch(`${process.env.NEXT_PUBLIC_CLOUDINARY_API}`, {
    method: 'POST',
    body: data,
  });
  const file = await res.json();
  setImageData({ ...imageData, images: file.secure_url });
  setLoading(false);
}

// ...

return (
  <div className="">
    {/* ... */}
    <input
      id="images"
      type="file"
      className="relative block w-full appearance-none rounded-none rounded-md border border-gray-300 px-3 py-2 text-gray-900 placeholder-gray-500 focus:z-10 focus:border-indigo-500 focus:outline-none focus:ring-indigo-500 sm:text-sm"
      accept="image/*"
      onChange={onImageSelect}
    />
    {/* ... */}
  </div>
);

现在,修改 addPost 函数,在添加帖子之前调用 uploadImage 函数:

async function addPost({
  title,
  description,
  slug,
  images,
  user_id,
}: {
  title: Posts['title'];
  description: Posts['description'];
  slug: Posts['slug'];
  images: Posts['images'];
  user_id: Posts['user_id'];
}) {
  try {
    setLoading(true);

    // Upload the image before adding a post
    await uploadImage();

    // ...

  } catch (error) {
    // ...
  } finally {
    // ...
  }
}


0
投票

要在点击“发布”按钮后上传图片,您只需将上传部分移到

addPost
内,也就是删除
uploadImage
,并在一个地方处理所有内容,在
submit
.

为此,设置一个

imageInputRef

const imageInputRef = useRef<HTMLInputElement>(null);

将其添加到您的上传输入(注意

onChange
被删除):

<input
  id="images"
  type="file"
  className="relative block w-full appearance-none rounded-none rounded-md border border-gray-300 px-3 py-2 text-gray-900 placeholder-gray-500 focus:z-10 focus:border-indigo-500 focus:outline-none focus:ring-indigo-500 sm:text-sm"
  accept="image/*"
  ref={imageInputRef}
/>

更改您的发布按钮如下(注意删除

onClick
,并添加
type=submit
):

<Button type="sumbit" className="mt-5 bg-red-500">
  Publish
</Button>

最后,删除

uploadImage
并将
addPost
更改为:

async function addPost({
  title,
  description,
  slug,
}: {
  title: Posts["title"];
  description: Posts["description"];
  slug: Posts["slug"];
  images: Posts['images'];
  user_id: Posts["user_id"];
}) {
  try {
    if (!imageInputRef.current?.files || imageInputRef.current?.files.length === 0) {
      // You could set some error message in a state here.
      return;
    }

    setLoading(true);

    const file = imageInputRef.current.files[0];
    const data = new FormData();
    data.append("file", file);
    data.append("upload_preset", "c_tags");

    const res = await fetch(`${process.env.NEXT_PUBLIC_CLOUDINARY_API}`, {
      method: "POST",
      body: data,
    });
    const returnedFile = await res.json();

    const newImageData = { ...imageData, images: returnedFile.secure_url };
    setImageData(newImageData);
    const updates = {
      title,
      description,
      slug,
      images: `${newImageData.images}`,
      created_at: new Date().toISOString(),
      user_id: userDetails?.id,
    };

    let { error } = await supabase.from("posts").upsert(updates);
    if (error) throw error;
    alert("Published!");
  } catch (error) {
    alert("Error updating the data!");
    console.log(error);
  } finally {
    setLoading(false);
  }
}

0
投票

我认为你可以通过创建一个单独的状态来存储图像,直到你点击发布按钮。

const [imgState, setImgState] = useState(null);

function onImgAdd(e){
// for input type file
setImgState(e.target.files[0])
}

function onPublishClick(){
// use upload logic for the file, use
uploadFunction(imgState)
}

如果您想在发布前显示预览而不必通过创建本地 url 实际上传它,这也为您提供了一种更简单的方法,这是许多网络应用程序中的常见功能,您以后可能最终需要它。

// Its a simple url that you can add as src of an img tag
const url = URL.createObjectURL(imgState);
© www.soinside.com 2019 - 2024. All rights reserved.