I have a run time error when trying to upload text/image in Next.JS web app

Viewed 33

I have an input that allows users to post with the option of uploading an image (following an online tutorial). The front end is Next.JS and Firestore for the database. I cannot work out the error. Any suggestions?

The posts seem to push to the database but I hit this error upon trying to submit via a button:

Run Time Error

then in the console:

console error.

The code is:

 /* eslint-disable @next/next/no-img-element */
import { EmojiHappyIcon, PhotographIcon } from '@heroicons/react/outline';
import {
  addDoc,
  collection,
  serverTimestamp,
  updateDoc,
} from 'firebase/firestore';
import { getDownloadURL, ref, uploadString } from 'firebase/storage';
import { useSession, signOut } from 'next-auth/react';
import { useRef, useState } from 'react';
import { db, storage } from '../firebase';

export default function Input() {
  const { data: session } = useSession();
  const [input, setInput] = useState('');
  const [selectedFile, setSelectedFile] = useState(null);
  const filePickerRef = useRef(null);

  const sendPost = async () => {
    const docRef = await addDoc(collection(db, 'posts'), {
      id: session.user.uid,
      text: input,
      userImg: session.user.image,
      timestamp: serverTimestamp(),
      name: session.user.name,
      username: session.user.username,
    });

    const imageRef = ref(storage, `posts/${docRef.id}/image`);

    if (setSelectedFile) {
      await uploadString(imageRef, selectedFile, 'data_url').then(async () => {
        const downloadURL = await getDownloadURL(imageRef);
        await updateDoc(doc(db, 'posts', docRef.id), {
          image: downloadURL,
        });
      });
    }

    setInput('');
  };

  const addImageToPost = (e) => {
    const reader = new FileReader();
    if (e.target.files[0]) {
      reader.readAsDataURL(e.target.files[0]);
    }

    reader.onload = (readerEvent) => {
      setSelectedFile(readerEvent.target.result);
    };
  };

  return (
    <>
      {session && (
        <div className="flex border-b border-gray-200 p-3 space-x-3">
          <img
            onClick={signOut}
            src={session?.user?.image}
            alt="user-img"
            className="h-11 w-11 rounded-full cursor-pointer hover:brightness-95"
          />
          <div className="w-full divide-y divide-gray-200">
            <div className="">
              <textarea
                className="w-full border-none focus:ring-0 text-lg placeholder-gray-700 tracking-wide min-h-[50px] text-gray-700 font-['Quattrocento']"
                rows="2"
                placeholder="Share a news article you want others to comment on!"
                value={input}
                onChange={(e) => setInput(e.target.value)}
              ></textarea>
            </div>
            <div className="flex items-center justify-between pt-2.5">
              <div className="flex">
                <div onClick={() => filePickerRef.current.click()}>
                  <PhotographIcon className="h-10 w-10 hoverEffect p-2 text-gray-500 hover:bg-gray-100 " />
                  <input
                    type="file"
                    hidden
                    ref={filePickerRef}
                    onClick={addImageToPost}
                  />
                </div>
                <EmojiHappyIcon className="h-10 w-10 hoverEffect p-2 text-gray-500 hover:bg-gray-100" />
              </div>
              <button
                onClick={sendPost}
                disabled={!input.trim()}
                className="bg-gray-500 text-white px-4 py-1.5 rounded font-bold shadow-md hover:brightness-95 disabled:opacity-50 font-['Quattrocento']"
              >
                Comment
              </button>
            </div>
          </div>
        </div>
      )}
    </>
  );
}

Upon running console.log(selectedFile) right before uploadString I get this: in response to Dharmaraj comment

1 Answers

As in the provided screenshot, the selectedFile is null. Try using onChange() to set image in state when selected and thenuploadBytes() to upload image as shown below:


function App() {
  const [selectedFile, setSelectedFile] = useState(null)

  const sendPost = async () => {
    const docRef = '.....' // update doc

    if (selectedFile) {
      const storageRef = ref(storage, `posts/${docRef.id}/image`)
      const snapshot = await uploadBytes(storageRef, selectedFile)

      console.log('Uploaded file:', snapshot.ref.fullPath)
    } 
  }
  
  return (
    <div className="App">
      <input type="file" id="fileInput" onChange={(e) => setSelectedFile(e.target.files[0])} />
      <button onClick={sendPost}>Send</button>
    </div>
  )
}
Related