Cannot read properties of undefined (reading *)

6.2k views Asked by At

Hey I am learning reactjs as much as i have learned I am trying to make note app my code given below

my App.js file

 import React , {useEffect, useState} from "react"
import { nanoid } from "nanoid"
import Editor from './Note/Editor'
import Sidebar from "./Note/Sidebar"


function App() {

  const [notes , setNotes] = useState(JSON.parse(localStorage.getItem("notes"))||[])
  const [currentNoteID , setCurrentNoteID] = useState(false)

  useEffect(()=>{
    localStorage.setItem("notes" , JSON.stringify(notes))
  },[notes])

  function createNewNotes(){
    const newNotes = {
      id: nanoid(),
      title:"untitled",
      body: "sdasda",
      lastModified: Date.now()
    }
    setNotes(prevNote => [newNotes , ...prevNote])
    setCurrentNoteID(newNotes.id)
  }

  function deleteNote(noteID){
    setNotes(prevNote => prevNote.filter(note=> note.id !== noteID ))
  }

  function getNotes(){

    return notes.find((note)=> note.id === currentNoteID)
  }
  return (
    <div className="note">
      <Sidebar
      notes={notes}
      createNewNotes={createNewNotes}
      currentNoteID={currentNoteID}
      setCurrentNoteID={setCurrentNoteID}
      deleteNote={deleteNote}
      />

      
            <Editor
              notes={getNotes()}
              currentNoteID={currentNoteID}/>
      
     

    </div>
   
  );
}

export default App;

my Sidebar.js file

 import React from 'react'
import './style.css'

export default function Sidebar(props){

    return(
        <>

            <div className='sidebar' >
                <div className='sidebar-header'>

                    <h3>Notes</h3>
                    <button className='add' onClick={props.createNewNotes} >Add</button>
                </div>
                   
           { props.notes.map((note)=>{
               return(

                   
                   <div key={note.id} 
                   className={`${note.id===props.currentNoteID ? "active" : ""}`} 
                   onClick={()=>props.setCurrentNoteID(note.id)}
                   >
                        <div>
                    <div className="sidebar-tab">

                        <div className='sidebar-title'>
                            <p className='title'>Untitled</p>
                        <button className='delete' onClick={()=>props.deleteNote(note.id)}>Delete</button>

                        </div>
                            <p className='note-preview'>summary of text</p>
                            
                    </div>
                    </div>

            </div>
            )
            })}
                </div>
        </>
    )
}

my Editor.js file

 import React , {useState} from "react";
import './style.css'

export default function Editor(props){

    const [edit , setEdit] = useState(props.notes) 

  function handleChange(event){
      const {name , value} = event.target
       setEdit(prevNote=> {
           return {
                ...prevNote,
                [name] : value

            }
        })

    }
    
if(!props.currentNoteID)
    return  <div className="no-note">no note active</div>
    return(
        <>
            <div className="main">
                <input type="text" className="main-input" name="title" placeholder="Enter title here" value={edit.title} onChange={handleChange}  autoFocus/>
                <textarea className="main-textarea" name="body"  placeholder="Type your notes" value={edit.body} onChange={handleChange} />
              


                <div className="preview">
                    <h1 className="preview-title">{edit.title}</h1>
                    <div className="main-preview">{edit.body}</div>
                </div>
                 

            </div>
        </>
    )
    
}

whenever i click add button or any sidebar button it shows me error

Uncaught TypeError: Cannot read properties of undefined (reading 'title')

please help me out how to fix this issue

1

There are 1 answers

1
a.h.g. On BEST ANSWER

You're expecting getNotes (which should probably be named getActiveNote, IMHO) to re-run every time notes or currentNoteID change.

To achieve this, you have to declare it as a callback (useCallback) and to declare its dependencies. Also you want to place the result in state (e.g: activeNote):

const getActiveNote = useCallback(
  () => notes.find((note) => note.id === currentNoteID),
  [notes, currentNoteID]
);
const [activeNote, setActiveNote] = useState(getActiveNote());
useEffect(() => {
  setActiveNote(getActiveNote());
}, [getActiveNote]);


   // ...
   <Editor note={activeNote} />

... at which point, you no longer need the currentNoteID in the <Editor /> as you can get it from props.note.id.

See it working here: https://codesandbox.io/s/crazy-glade-qb94qe?file=/src/App.js:1389-1448

Note: the same thing needs to happen in <Editor>, when note changes:

useEffect(() => setEdit(note), [note]);