skip to Main Content

I am encountering an error in my React app:

react-dom.development.js:15408 Uncaught Error: Invalid hook call. Hooks can only be called inside of the body of a function component.
This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app

Things I’ve tried to fix the issue:

  1. Versions of React and React DOM: I checked that both react and react-dom are at the same version in my package.json.
  2. Following the Rules of Hooks: I have ensured that I am using hooks (like useState) correctly inside the body of the functional component. There are no hooks inside conditionals or nested functions.
  3. Multiple Copies of React: I used npm ls react to ensure there’s only one instance of React in my node_modules, and it looks correct.

Additional information:

  • I’m not using npm link or any local linking methods, so the issue shouldn’t be related to multiple copies of React.
  • This issue happens even on the first render when I try to call the HandleSave function.

The error targets these two lines:

  1. Storage component:

    const [entries, setEntries] = useState<{ title: string, description: string, status: string }[]>([]);
    
  2. JournalEntry component:

     const HandleSave = () => {
    
            if(inputValue.trim() && textareaValue.trim()){
                onSave({
                    title: inputValue,
                    description: textareaValue,
                    status: status,
                });
    
                setInputValue('')
                setTextAreaValue('')
                setStatus('Pending')
            }
    
            else{
                alert('Please fill out both the title and description!');
            }
    

Here’s my code for routes:

import Home from "./pages/home";
import PrayerJournal from "./pages/prayerjournal";
import JournalEntry from "./pages/journalentry";
import Storage from "./components/Storage";

const routes = [
    {
        path: "/",
        element: <Home/>
    },
    {
        path: "prayer",
        element: <PrayerJournal/>
    },

    {
        path: "journalentry",
        element: <JournalEntry onSave={Storage}/>
    },
 

]

export default routes;

Here’s my code for Storage component:

import { useState, useEffect} from "react";
import JournalEntry from "../pages/journalentry";

const Storage = () => {
const [entries, setEntries] = useState<{ title: string, description: string, status: string }[]>([]);

const saveEntry = (entry: { title: string, description: string, status: string }) => {
    
    console.log("saveEntry invoked:", entry);
    setEntries(prevEntries => {
        const updatedEntries = [...prevEntries, entry]
    try {
        localStorage.setItem("journalEntries", JSON.stringify(updatedEntries));
        console.log("Entries saved to localStorage:", updatedEntries);
      } catch (error) {
        console.error("Failed to save to localStorage:", error);
      }

      return updatedEntries;
});
};

useEffect(() => {
const savedEntries = localStorage.getItem('journalEntries')
if (savedEntries) {
    try {
        const parsedEntries = JSON.parse(savedEntries);
        setEntries(parsedEntries);
        console.log("Entries loaded from localStorage:", parsedEntries);
      } catch (error) {
        console.error("Failed to parse localStorage data:", error);
      }
    }
    else {
        console.log("No saved entries found in localStorage.");
    }

}, [])
console.log("saveEntry function being passed:", saveEntry);

return(
    <div>
        <JournalEntry onSave={saveEntry} />

        <h2>saved entries</h2>
        <ul>
        {entries.map((entry, index) => {
            return (
            <li key={index}>
                <h3>{entry.title}</h3>
                <p>{entry.description}</p>
                <p>Status: {entry.status}</p>
            </li>)
})}
        </ul>
       
    </div>
)
};
export default Storage;

and here’s my code from JournalEntry component:

import React from "react";
import { useState } from "react";

const JournalEntry = ({onSave}: { onSave: (entry: { title: string, description: string, status: string }) => void }) => {
    console.log('onSave prop:', onSave);
    const [inputValue, setInputValue] = useState('');
    const [textareaValue, setTextAreaValue] = useState('');
    const [status, setStatus] = useState('Pending');

    const ChangeValue = (e: React.ChangeEvent<HTMLInputElement>) => setInputValue(e.target.value);
    const TextAreaChangeValue = (e: React.ChangeEvent<HTMLTextAreaElement>) => setTextAreaValue(e.target.value);
    const ChangeStatus = () => {
        setStatus(prevStatus => prevStatus === 'Pending' ? 'Answered' : 'Pending');
    }


    const HandleSave = () => {
        
        if(inputValue.trim() && textareaValue.trim()){
            onSave({
                title: inputValue,
                description: textareaValue,
                status: status,
            });

            setInputValue('')
            setTextAreaValue('')
            setStatus('Pending')
        }

        else{
            alert('Please fill out both the title and description!');
        }
    }
    return(
        <>
        <div className="bg-customYellow h-screen ">
        <nav className="w-full flex justify-between items-center absolute">
<div className="text-[3rem] font-belle relative top-2 text-textBlackish ml-5">Love, Jesus</div>
<div className="flex gap-[3rem] mr-5 relative top-2">
    <div className="flex gap-3 ">
    <button className="text-white font-annie bg-customBrown pt-1 pb-1 pr-6 pl-6 rounded-2xl">Cancel</button>
    <button className="text-white font-annie bg-customBrown pt-1 pb-1 pr-6 pl-6 rounded-2xl" onClick={HandleSave}>Save</button>
    </div>
    <div className="w-8 h-8 bg-slate-300 rounded-2xl "></div>
</div>
        </nav>
<div className="w-full h-[80%] relative top-[5rem] flex justify-center items-center ">
<div className="flex-col w-[50%] gap-5 h-full flex justify-center items-start relative ">
    <div className="relative flex  items-center w-full">
    <input 
          className="z-20 peer bg-transparent focus: outline-none w-full h-10 focus:text-[3rem] focus:text-textBlackish focus:font-annie relative"
          type="text"
          value= {inputValue}
          onChange={ChangeValue}
          placeholder= ''
          />
          <span className="text-[3rem] z-10 font-annie text-textBlackish absolute peer-focus:hidden">Title</span>
    </div>
         
          <button 
          className="pt-1 pb-1 pl-7 pr-7 bg-customBrown text-white rounded-2xl font-annie text-[1rem]"
          onClick={ChangeStatus}>{status}</button>
          <textarea
          className="w-full h-full placeholder:text-textBlackish placeholder:font-annie focus:text-textBlackish focus:font-annie bg-transparent focus:outline-none"
          value= {textareaValue}
          onChange={TextAreaChangeValue}
          placeholder= 'It is time to seek the Lord :)'
          />
        </div>
</div>


        
        </div>
        </>
    )
}

export default JournalEntry;

2

Answers


  1. can’t you change

     {
            path: "journalentry",
            element: <JournalEntry onSave={Storage}/>
        },
    

    to

    {
        path: "journalentry",
        element: <Storage/>  // Render Storage component directly
    }
    

    because inside Storage Component, JournalEntry is rendered.

    According to your code,

    Storage is the parent component and it manage state and also has the save method.
    JournalEntry is a child component inside it

    Login or Signup to reply.
  2. Main issue with your code is in your routes you are trying to pass Storage component as a prop instead of function.

    {
         path: "journalentry",
         element: <Storage />
    }
    

    As your storage component already using the JournalEntry component it will work as expected.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search