Next.js react-sortablejs with local images

42 views Asked by At

I have a problem that when I use react-sortablejs with local images, the src to image is not display. The code:

import { useState } from "react";
import axios from "axios";
import { useRouter } from "next/router";
import Image from "next/image";
import Spinner from "./Spinner";
import { ReactSortable } from "react-sortablejs";

export default function ProductForm({
  _id,
  title: existingTitle,
  description: existingDescription,
  price: existingPrice,
  images: existingImages,
}) {
  const [title, setTitle] = useState(existingTitle || "");
  const [description, setDescription] = useState(existingDescription || "");
  const [price, setPrice] = useState(existingPrice || "");
  const [images, setImages] = useState(existingImages || []);
  const [goToProducts, setGoToProducts] = useState("");
  const [isUploading, setIsUploading] = useState(false);
  const router = useRouter();
  async function saveProduct(ev) {
    ev.preventDefault();
    const data = { title, description, price, images };
    if (_id) {
      await axios.put("/api/products", { ...data, _id });
    } else {
      await axios.post("/api/products", data);
    }
    setGoToProducts(true);
  }
  if (goToProducts) {
    router.push("/products");
  }
  async function uploadImages(ev) {
    const files = ev.target?.files;
    if (files?.length > 0) {
      setIsUploading(true);
      const data = new FormData();
      for (const file of files) {
        data.append("file", file);
      }
      const res = await axios.post("/api/upload", data);
      for (let i = 0; i < res.data.path.length; i++) {
        console.log(res.data.path[i]);
      }
      setImages((OldImages) => {
        return [...OldImages, ...res.data.path];
      });
      setIsUploading(false);
    }
  }
  function updateImagesOrder(images) {
    setImages(images);
  }
  return (
    <form onSubmit={saveProduct}>
      <label>Nazwa Produkt</label>
      <input
        type="text"
        placeholder="Nazwa Produktu"
        value={title}
        onChange={(ev) => setTitle(ev.target.value)}
      />
      <label>Opis Produktu</label>
      <textarea
        placeholder="Opis"
        value={description}
        onChange={(ev) => setDescription(ev.target.value)}
      ></textarea>
      <label>Zdjęcia Produktu</label>
      <div className="mb-2 flex flex-wrap gap-1">
        <ReactSortable
          list={images}
          className="flex flex-wrap gap-1"
          setList={updateImagesOrder}
        >
          {images?.length &&
            images.map((path) => (
              <div key={path}>
                <Image
                  src={path}
                  alt="product_image"
                  width={0}
                  height={0}
                  sizes="6rem"
                  style={{ width: "100%", height: "100px" }}
                  className="rounded-lg"
                />
              </div>
            ))}
        </ReactSortable>
        {isUploading && (
          <div className="h-24 flex items-center">
            <Spinner />
          </div>
        )}
        <label className="w-24 h-24 cursor-pointer text-center flex items-center justify-center text-sm gap-1 text-gray-500 rounded-lg bg-gray-200">
          <svg
            xmlns="http://www.w3.org/2000/svg"
            fill="none"
            viewBox="0 0 24 24"
            strokeWidth={1.5}
            stroke="currentColor"
            className="w-6 h-6"
          >
            <path
              strokeLinecap="round"
              strokeLinejoin="round"
              d="M3 16.5v2.25A2.25 2.25 0 0 0 5.25 21h13.5A2.25 2.25 0 0 0 21 18.75V16.5m-13.5-9L12 3m0 0 4.5 4.5M12 3v13.5"
            />
          </svg>
          <div>Wgraj</div>
          <input
            type="file"
            onChange={uploadImages}
            className="hidden"
            multiple
          ></input>
        </label>
      </div>
      <label>Cena Produktu</label>
      <input
        type="number"
        placeholder="Cena"
        value={price}
        onChange={(ev) => setPrice(ev.target.value)}
      />
      <button type="submit" className="btn-primary">
        Zapisz Produkt
      </button>
    </form>
  );
}

Images work normal when function don't do anything:

function updateImagesOrder(images) {
    console.log(images);
  }

and in console display: (2) [String, String] 0 : String {'/2024-02-07T15-43-47.272Z-Fundamental.png', chosen: false, selected: false} 1 : String {'/2024-02-07T15-43-47.260Z-css-flexbox-poster.png', chosen: false, selected: false} length : 2

But when I want implement react-sortablejs by changing:

function updateImagesOrder(images) {
    setImages(images);
  }

Images don't work and I have error in console: Image is missing required "src" property:

How to use this react-sortablejs with local images?

1

There are 1 answers

0
famgz On

react-sortablejs is somehow converting the urls.

I managed to fix by converting it back to regular strings using either:

src={String(url)}

or

src={`${url}`}