Passing function as a prop is not working (react)

81 Views Asked by At

I am trying to call handleAddNote which is passed from App.js to AddNote.js but I am not able to reference it in AddNote without getting error saying handleAddNote is not a function. I have tried multiple ways but I always get it is not a function or is not defined. In the tutorial I'm following it says to simply call handleAddNote(noteText) in handleSave function but I am not able to.

import Note from './Components/Note';
import NotesList from './Components/NotesList';
import { useState } from 'react';
import { nanoid } from 'nanoid';


const App = () => {

  const[notes, setNotes] = useState([
    {
      id: nanoid(),
      text: "My first note",
      date: "2/12/2024"
    },
    {
      id: nanoid(),
      text: "My second note",
      date: "2/13/2024"
    },
    {
      id: nanoid(),
      text: "My third note",
      date: "2/14/2024"
    },
    {
      id: nanoid(),
      text: "My fourth note",
      date: "2/15/2024"
  }])

  const addNote = (text) => {
        console.log(text);
  }

  return (
    <div className="container">
      <NotesList notes={notes} handleAddNote={addNote}/>
    </div>

  );
}

export default App;
import Note from './Note';
import AddNote from './AddNote';

const NotesList = ({ notes, handleAddNote }) => {
    return (
        <div className="notes-list">
            {notes.map((note)=> 
                <Note id={note.id} text={note.text} date={note.date}/>
            )}

            <AddNote handleAddNote={handleAddNote}/>
        </div>
    );
}

export default NotesList;
import { useState } from 'react';

const AddNote = ({ handleAddNote }) => {
    const [noteText, setNoteText] = useState("");

    const handleChange = (event) => {
        setNoteText(event.target.value);
    };

    const handleSave = () => {
        console.log({handleAddNote});
        {handleAddNote(noteText)};
    };

    return(
        <div className="note new">
            <textarea 
                onChange={handleChange}
                value={noteText}
                className='new-note'
                rows="8" 
                columns="10" 
                placeholder="Type to add a note...">
            </textarea>

            <div className="note-footer">
                <small>200 remaining</small>
                <button className='save' onClick={(noteText)=>handleAddNote(noteText)}>Save</button>
            </div>
        </div>

    );
}

export default AddNote

2

There are 2 best solutions below

1
eternalyogi On

In the above code, it looks like the function is passed correctly to the child component.

Remove the curly braces {handleAddNote(noteText)} from handleSave function.

Just use handleAddNote(noteText) and it should work.

2
Dom On

I do not see how your project is laid out but made assumptions based on the import statements. We'll go file by file.

Before we start, the most important thing is TO ALWAYS have import React from 'react'; at the top of your component files otherwise it'll lead to problems.

The directory structure I made looks like so:

  • src
    • Components
      • AddNote.js
      • Note.js (not provided)
      • NotesList.js
    • App.js
    • index.js (runs App.js)

I do not know what is calling App so I created the file, index.js to handle that.


TLDR: Here is a demo - https://codesandbox.io/p/sandbox/pseudo-class-sticker-sheet-forked-43k4pg


Let's start with:

index.js

import { createRoot } from "react-dom/client";
import App from "./App";

// Clear the existing HTML content
document.body.innerHTML = '<div id="app"></div>';

// Render your React component instead
const root = createRoot(document.getElementById("app"));
root.render(<App />);

Pretty self explanatory. Creates the div and renders the App component against it.

App.js

import React, { useState } from "react";
import NotesList from "./Components/NotesList";
import { nanoid } from "nanoid";
const App = () => {
  const [notes, setNotes] = useState([
    {
      id: nanoid(),
      text: "My first note",
      date: "2/12/2024",
    },
    {
      id: nanoid(),
      text: "My second note",
      date: "2/13/2024",
    },
    {
      id: nanoid(),
      text: "My third note",
      date: "2/14/2024",
    },
    {
      id: nanoid(),
      text: "My fourth note",
      date: "2/15/2024",
    },
  ]);

  const addNote = (text) => {
    const newNote = {
      id: nanoid(),
      text: text,
      date: new Date().toDateString(),
    };
    setNotes([...notes, newNote]);
  };

  return (
    <div className="container">
      <NotesList notes={notes} handleAddNote={addNote} />
    </div>
  );
};

export default App;

I made no changes here outside of adding setNotes so the App renders.

NotesList.js

import React from "react";
import Note from "./Note";
import AddNote from "./AddNote";

const NotesList = ({ notes, handleAddNote }) => {
  return (
    <div className="notes-list">
      {notes.map((note) => (
        <Note key={note.id} id={note.id} text={note.text} date={note.date} />
      ))}
      <AddNote handleAddNote={handleAddNote} />
    </div>
  );
};

export default NotesList;

Along with adding import React from 'react' I made sure to use key property so each element in the array is treated separately.

AddNote.js

import React, { useState } from "react";

const AddNote = ({ handleAddNote }) => {
  const [noteText, setNoteText] = useState("");

  const handleChange = (event) => {
    setNoteText(event.target.value);
  };

  const handleSave = () => {
    handleAddNote(noteText);
  };

  return (
    <div className="note new">
      <textarea
        onChange={handleChange}
        value={noteText}
        className="new-note"
        rows="8"
        columns="10"
        placeholder="Type to add a note..."
      ></textarea>

      <div className="note-footer">
        <small>200 remaining</small>
        <button className="save" onClick={handleSave}>
          Save
        </button>
      </div>
    </div>
  );
};

export default AddNote;

Most changes took place here. I changed the onClick so it points to the handleSave function. Originally, you had: onClick={(noteText)=>handleAddNote(noteText)} which is incorrect since nodeText is actually a MouseEvent (onClick = (e: MouseEvent)).

Hope this clears things up and let me know if you have any issues/concerns.