Function components cannot have string refs. We recommend using useRef() instead - with Nextjs

85 Views Asked by At

I use Next.js and I'm just trying to add data to the server normally using the fetch method and thunk, but I get this error:

Error: Function components cannot have string refs. We recommend using useRef() instead.

addBookForm.jsx:

import React, {useRef} from 'react'
import { useDispatch } from 'react-redux'
import { insertBook } from '@/app/redux/Features/bookSlice'
function addBooksForm() {
  // ref
  const title = useRef(null);
  const img = useRef(null);
  const descripition = useRef(null);
  const bookLinke = useRef(null);
  
  const dispatch = useDispatch();

  const handelSubmit = (e) => {
    e.preventDefault();

    const data = {
      name: title.current.value,
      photo: img.current.value,
      des: descripition.current.value,
      link: bookLinke.current.value
    };

    dispatch(insertBook(data));

    title.current.value = null;
    img.current.value = null;
    descripition.current.value = null;
    bookLinke.current.value = null;
  }

  return (
    <div className="w-full  px-4 bg-white ">
    <div className="max-w-[700px] mx-auto">
      <div className="w-full shadow-xl flex flex-col p-4 my-8 rounded-lg ">
        <h2 className="text-2xl font-bold text-center py-4">Insert Book</h2>
        <form onSubmit={handelSubmit}>
          <div className="mb-6">
            <label className="block mb-2 text-l font-medium text-gray-900">
              Title
            </label>
            <input
              type="text"
              className="block w-full p-4 mb-4 text-gray-900 border border-gray-300 rounded-lg bg-gray-50 sm:text-md "
              ref={name}
              required
            ></input>
            <label className="block mb-2 text-l font-medium text-gray-900">
            Linke of image
          </label>
          <input
            type="text"
            className="block w-full p-4 mb-4 text-gray-900 border border-gray-300 rounded-lg bg-gray-50 sm:text-md "
            ref={img}
            required
          ></input>
            <label className="block mb-2 text-sm font-medium text-gray-900 ">
              Description
            </label>
            <textarea
              id="message"
              rows={4}
              className="block p-2.5 w-full text-sm mb-4 text-gray-900 bg-gray-50 rounded-lg border border-gray-300"
              
              ref={descripition}
              required
            ></textarea>
            <label className="block mb-2 text-l font-medium text-gray-900">
            Linke of book
          </label>
          <input
            type="text"
            className="block w-full p-4 mb-4 text-gray-900 border border-gray-300 rounded-lg bg-gray-50 sm:text-md "
            ref={bookLinke}
            required
          ></input>
            <button
              type="submit"
              className="text-white bg-[#BDA175] hover:bg-black disabled:bg-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 mr-2 mb-2" 
            >
              Submit
            </button>
          </div>
        </form>
      </div>
    </div>
  </div>
  )
}

export default addBooksForm

bookSlice:

"use client";
import { createSlice, createAsyncThunk } from "@reduxjs/toolkit";

export const insertBook = createAsyncThunk(
  "book/insertBook",
  async (bookData, thunkAPI) => {
    const { rejectWithValue } = thunkAPI;

    try {
      const res = await fetch("http://localhost:3005/books", {
        method: "POST",
        body: JSON.stringify(bookData),
        headers: {
          "Content-type": "application/json; charset=UTF-8",
        },
      });
      const data = await res.json();
      return data;
    } catch (error) {
      return rejectWithValue(error.message);
    }
  }
);

const bookSlice = createSlice({
  name: "book",
  initialState: { books: [], isLoading: false, error: null },
  reducers: {},
  extraReducers: (builder) => {

      // INSERT BOOK
      builder.addCase(insertBook.pending, (state, action) => {
        state.isLoading = true;
        state.error = null;
      }),
      builder.addCase(insertBook.fulfilled, (state, action) => {
        state.isLoading = false;
        state.books.push(action.payload);
      }),
      builder.addCase(insertBook.rejected, (state, action) => {
        state.isLoading = false;
        state.error = action.payload;
      });
  },
});

export default bookSlice.reducer;

How can I fix that error?

1

There are 1 best solutions below

3
On

I suspect the issue is the ref={name} of the title input, there is no defined name variable though, so I'd expect to see a different error. You likely meant to use ref={title}.

That said, the implementation is a bit of an anti-pattern. It is generally preferable to use fully controlled inputs using React state and onChange handlers on the inputs, or to use uncontrolled inputs and gather the form field values from the form element's onSubmit event object.

Examples

  • Controlled inputs

    function addBooksForm() {
      const [title, setTitle] = useState("");
      const [img, setImg] = useState("");
      const [description, setDescription] = useRef("");
      const [bookLink, setBookLink] = useRef("");
    
      const dispatch = useDispatch();
    
      const handleSubmit = (e) => {
        e.preventDefault();
    
        const data = {
          name: title,
          photo: img,
          des: description,
          link: bookLink,
        };
        dispatch(insertBook(data));
    
        setTitle("");
        setImg("");
        setDescription("");
        setBookLink("");
      };
    
      return (
        <div className="w-full  px-4 bg-white ">
          <div className="max-w-[700px] mx-auto">
            <div className="w-full shadow-xl flex flex-col p-4 my-8 rounded-lg ">
              <h2 className="text-2xl font-bold text-center py-4">Insert Book</h2>
              <form onSubmit={handelSubmit}>
                <div className="mb-6">
                  <label className="block mb-2 text-l font-medium text-gray-900">
                    Title
                  </label>
                  <input
                    type="text"
                    className="block w-full p-4 mb-4 text-gray-900 border border-gray-300 rounded-lg bg-gray-50 sm:text-md"
                    value={title}
                    onChange={e => setTitle(e.target.value)}
                    required
                  />
                  <label className="block mb-2 text-l font-medium text-gray-900">
                    Linke of image
                  </label>
                  <input
                    type="text"
                    className="block w-full p-4 mb-4 text-gray-900 border border-gray-300 rounded-lg bg-gray-50 sm:text-md "
                    value={img}
                    onChange={e => setImg(e.target.value)}
                    required
                  >
                  <label className="block mb-2 text-sm font-medium text-gray-900 ">
                    Description
                  </label>
                  <textarea
                    id="message"
                    rows={4}
                    className="block p-2.5 w-full text-sm mb-4 text-gray-900 bg-gray-50 rounded-lg border border-gray-300"
                    value={description}
                    onChange={e => setDescription(e.target.value)}
                    required
                  />
                  <label className="block mb-2 text-l font-medium text-gray-900">
                    Linke of book
                  </label>
                  <input
                    type="text"
                    className="block w-full p-4 mb-4 text-gray-900 border border-gray-300 rounded-lg bg-gray-50 sm:text-md "
                    value={bookLink}
                    onChange={e => setBookLink(e.target.value)}
                    required
                  />
                  <button
                    type="submit"
                    className="text-white bg-[#BDA175] hover:bg-black disabled:bg-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 mr-2 mb-2" 
                  >
                    Submit
                  </button>
                </div>
              </form>
            </div>
          </div>
        </div>
      )
    }
    
  • Uncontrolled inputs:

    function addBooksForm() {
      const dispatch = useDispatch();
    
      const handleSubmit = (e) => {
        e.preventDefault();
    
        const data = {
          name: e.target.title.value,
          photo: e.target.photo.value,
          des: e.target.description.value,
          link: e.target.bookLink.value,
        };
        dispatch(insertBook(data));
    
        e.target.reset();
      };
    
      return (
        <div className="w-full  px-4 bg-white ">
          <div className="max-w-[700px] mx-auto">
            <div className="w-full shadow-xl flex flex-col p-4 my-8 rounded-lg ">
              <h2 className="text-2xl font-bold text-center py-4">Insert Book</h2>
              <form onSubmit={handelSubmit}>
                <div className="mb-6">
                  <label className="block mb-2 text-l font-medium text-gray-900">
                    Title
                  </label>
                  <input
                    type="text"
                    className="block w-full p-4 mb-4 text-gray-900 border border-gray-300 rounded-lg bg-gray-50 sm:text-md"
                    name="title"
                    required
                  />
                  <label className="block mb-2 text-l font-medium text-gray-900">
                    Linke of image
                  </label>
                  <input
                    type="text"
                    className="block w-full p-4 mb-4 text-gray-900 border border-gray-300 rounded-lg bg-gray-50 sm:text-md "
                    name="photo"
                    required
                  >
                  <label className="block mb-2 text-sm font-medium text-gray-900 ">
                    Description
                  </label>
                  <textarea
                    id="message"
                    rows={4}
                    className="block p-2.5 w-full text-sm mb-4 text-gray-900 bg-gray-50 rounded-lg border border-gray-300"
                    name="description
                    required
                  />
                  <label className="block mb-2 text-l font-medium text-gray-900">
                    Linke of book
                  </label>
                  <input
                    type="text"
                    className="block w-full p-4 mb-4 text-gray-900 border border-gray-300 rounded-lg bg-gray-50 sm:text-md "
                    name="bookLink"
                    required
                  />
                  <button
                    type="submit"
                    className="text-white bg-[#BDA175] hover:bg-black disabled:bg-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 mr-2 mb-2" 
                  >
                    Submit
                  </button>
                </div>
              </form>
            </div>
          </div>
        </div>
      )
    }