Вопрос или проблема
У меня есть форма для создания/редактирования блога, я добавил свойство isEditing, чтобы, например, изменить текст кнопки отправки с “создать” на “обновить”, если isEditing истинно. Таким образом, мне не нужно будет создавать отдельные формы для обеих этих страниц.
Когда я создаю пост, заголовок, описание и изображение блога добавляются в базу данных, и всё работает как ожидается. Но когда я пытаюсь изменить изображение определенного блога и отправляю форму, заголовок и описание можно обновить, но изображение не обновляется, оно остается прежним, как будто не было обновлено в базе данных вообще.
Я использую prisma + postgresql, nextjs и cloudinary для хранения изображений.
Давайте найдем отличия между страницами редактирования и создания блога:
Компонент EditBlogPage (/app/(pages)/edit/[id]):
"use client";
import FormBlog from "@/components/blogPage/FormBlog";
import { useMutation, useQuery } from "@tanstack/react-query";
import axios from "axios";
import { useRouter } from "next/navigation";
import { useState } from "react";
const EditBlogPage = ({ params }) => {
const { id } = params;
const router = useRouter();
const [imageUrl, setImageUrl] = useState("");
const { data: dataBlog, isLoading } = useQuery({
queryKey: ["blogs", id],
queryFn: async () => {
const res = await axios.get(`/api/blogs/${id}`);
return res.data;
},
});
const { mutate: updateBlog } = useMutation({
mutationFn: (newBlog) => {
return axios.patch(`/api/blogs/${id}`, newBlog);
},
onError: (error) => {
console.error(error);
},
onSuccess: () => {
router.push("/blog");
router.refresh();
},
});
const handleEditBlog = (data) => {
updateBlog({ ...data, imageUrl: imageUrl || dataBlog.imageUrl });
};
if (isLoading) {
return <h1 className="h-screen text-3xl text-center">Загрузка...</h1>;
}
return (
<div className="h-screen flex items-center justify-center flex-col">
<h1 className="mb-4 text-lg">Изменить блог</h1>
<FormBlog
submit={handleEditBlog}
initialValue={dataBlog}
isEditing
setImageUrl={setImageUrl}
/>
</div>
);
};
export default EditBlogPage;
Компонент CreateBlog (/app/(pages)/create/page.jsx):
"use client";
import { useMutation } from "@tanstack/react-query";
import FormBlog from "../../../components/blogPage/FormBlog";
import axios from "axios";
import { useRouter } from "next/navigation";
import { useState } from "react";
const CreateBlog = () => {
const [imageUrl, setImageUrl] = useState("");
const router = useRouter();
const handleCreateBlog = (data) => {
const blogData = {
...data,
imageUrl: imageUrl,
};
createBlog(blogData);
};
const { mutate: createBlog, isPending } = useMutation({
mutationFn: (newBlog) => {
return axios.post("/api/blogs/create", newBlog);
},
onError: (error) => {
console.error(error);
},
onSuccess: () => {
router.push("/blog");
router.refresh();
},
});
return (
<div className="h-screen flex items-center justify-center flex-col">
<FormBlog
submit={handleCreateBlog}
initialValue={null}
isEditing={false}
setImageUrl={setImageUrl}
/>
</div>
);
};
export default CreateBlog;
А теперь FormBlog.jsx, я попытался убрать ненужные части кода, чтобы вы могли сосредоточиться на проблеме:
"use client";
import { CldUploadButton } from "next-cloudinary";
import Image from "next/image";
import { useEffect, useState } from "react";
import { useForm } from "react-hook-form";
const FormBlog = ({ submit, isEditing, initialValue, setImageUrl }) => {
const { register, handleSubmit } = useForm({
defaultValues: initialValue,
});
const [publicId, setPublicId] = useState("");
// Установите URL изображения, когда находитесь в режиме редактирования
useEffect(() => {
if (isEditing && initialValue?.imageUrl) {
const parts = initialValue.imageUrl.split("/");
const publicIdPart = parts[parts.length - 1].split(".")[0]; // Извлеките публичный ID из URL
setPublicId(publicIdPart);
setImageUrl(initialValue.imageUrl); // Установите URL изображения для редактирования
}
}, [isEditing, initialValue, setImageUrl]);
const handleImageUpload = (result) => {
const info = result.info;
if ("secure_url" in info && "public_id" in info) {
const url = info.secure_url;
const public_id = info.public_id;
setImageUrl(url);
setPublicId(public_id);
}
};
const removeImg = async (e) => {
e.preventDefault();
try {
const res = await fetch("/api/blogs/removeImage", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ publicId }),
});
if (res.ok) {
await res.json();
setImageUrl("");
setPublicId("");
} else {
console.error("Ошибка при удалении изображения");
}
} catch (error) {
console.log(error);
}
};
return (
<form
className="w-1/2 mx-auto p-6 bg-white rounded-lg shadow-md"
onSubmit={handleSubmit(submit)}
>
<div className="flex items-center justify-center flex-col">
<CldUploadButton
uploadPreset={`${process.env.NEXT_PUBLIC_CLOUDINARY_UPLOAD_PRESET}`}
className={`relative h-48 w-full border-2 my-4 border-dotted grid place-items-center bg-slate-100 rounded-md ${
publicId && "pointer-events-none"
}`}
onSuccess={handleImageUpload}
>
{!publicId && "Загрузить фото"}
{publicId && (
<Image
src={`https://res.cloudinary.com/${process.env.NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME}/image/upload/${publicId}`}
fill
className="absolute object-cover inset-0"
alt={publicId}
/>
)}
</CldUploadButton>
{publicId && (
<button
onClick={removeImg}
className="p-4 rounded-lg bg-red-600 text-white my-4"
>
Убрать изображение
</button>
)}
</div>
<button
type="submit"
className="w-full bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:ring-2 focus:ring-blue-500"
>
{isEditing ? "Обновить" : "Создать"}
</button>
</form>
);
};
export default FormBlog;
Сообщите мне, какую часть кода следует добавить к этому вопросу, возможно, роутеры API и т.д.
Ответ или решение
Чтобы решить проблему с обновлением изображения на странице редактирования блога, давайте внимательно рассмотрим предполагаемую модель данных и обработку API CRUD. Похоже, что логика обновления изображения может быть нарушена в API-методе, который обрабатывает PATCH-запрос.
1. Проверка текущего API для обновления блога
Во-первых, убедитесь, что ваш API-метод, который отвечает за обновление блога, правильно обрабатывает обновления изображения. Например, если вы используете Prisma
, ваш метод может выглядеть так:
// pages/api/blogs/[id].js
import db from '../../../lib/prisma'; // Путь к вашей установке Prisma
export default async function handler(req, res) {
const {
method,
query: { id },
body,
} = req;
switch (method) {
case 'PATCH':
try {
const updatedBlog = await db.blog.update({
where: { id: Number(id) },
data: {
title: body.title,
description: body.description,
imageUrl: body.imageUrl, // Проверьте, корректно ли здесь указано поле для обновления
},
});
return res.status(200).json(updatedBlog);
} catch (error) {
return res.status(500).json({ error: 'Ошибка при обновлении блога' });
}
default:
return res.status(405).end(); // Method Not Allowed
}
}
2. Проверка на клиенте
Убедитесь, что ваша логика, передающая новые данные на сервер, действительно передает новое поле imageUrl
. В вашем компоненте EditBlogPage
вы правильно передаете imageUrl
как часть объекта данных, но для этого важно, чтобы imageUrl
действительно обновлялся при загрузке нового изображения.
3. Убедитесь, что handleImageUpload
работает корректно
В компоненте FormBlog
, логика обработки загрузки изображения выглядит нормально, но убедитесь, что метод handleImageUpload
действительно вызывается и устанавливает новый URL изображения:
const handleImageUpload = (result) => {
const info = result.info;
if ("secure_url" in info && "public_id" in info) {
const url = info.secure_url;
const public_id = info.public_id;
setImageUrl(url); // Установите новый URL
setPublicId(public_id); // Установите новый public_id
}
};
4. Логирование и отладка
Добавьте отладочные журналы в ваш метод обработки submit
как в клиенте, так и на сервере, чтобы убедиться, что в ваши данные действительно попадает новый imageUrl
. Например:
const handleEditBlog = (data) => {
console.log('Data being submitted:', { ...data, imageUrl: imageUrl || dataBlog.imageUrl });
updateBlog({ ...data, imageUrl: imageUrl || dataBlog.imageUrl });
};
Убедитесь, что imageUrl
обновляется, когда вы загружаете новое изображение и передаете правильные данные на сервер для обновления.
5. Дополнительная проверка в FormBlog
В компоненте FormBlog
, когда вы загружаете новое изображение, проверьте, что код в useEffect
не перезаписывает значение imageUrl
, если оно обновляется:
useEffect(() => {
if (isEditing && initialValue?.imageUrl) {
setImageUrl(initialValue.imageUrl);
}
}, [isEditing, initialValue]); // Убедитесь, что здесь не пересоздается состояние
Заключение
Если вышеуказанные шаги не приводят к решению вашей проблемы, предоставьте дополнительные детали о вашем API или конкретные ошибки, с которыми вы сталкиваетесь. Это поможет лучше понять проблему. Также не забудьте проверить все возможные коды состояния HTTP, чтобы лучше понять, откуда может исходить ошибка.