Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cannot read properties of undefined (reading 'find')

I'm doing a CRUD with redux. The application has a form with a username, and then a screen to create, view, edit and delete posts and I'm doing the editing part. However, when I use array.find() to traverse the array and look for each post, I'm getting this error:

Cannot read properties of undefined (reading 'find')

I will leave the codes below.

EditScreen:

import React, {useState} from 'react';
import '../_assets/modal.css';
import { useDispatch, useSelector } from 'react-redux';
import { useLocation, useNavigate } from 'react-router-dom'

import {editPost} from '../redux/postsslice';

function EditModal({ closeModal}) {

    const { pathname } = useLocation();
    const postId = pathname.replace("/edit-post/", "")
    const post  = useSelector((state) => state.posts.find((post) => post.id === postId))
    const dispatch = useDispatch()
    const navigation = useNavigate();

    const [title, setTitle] = useState(post.title)
    const [content, setContent] = useState(post.content)

    

    const onTitleChanged = e => setTitle(e.target.value)
    const onContentChanged = e => setContent(e.target.value)

    const onSavePostClicked = (e) => {
        if (title && content) {
            dispatch(editPost({id: postId, title, content}))
        }
           
    }


    return (
        <div className="modalBackground">
            <div className="modalContainer">
                <div className="title"><h1>Edit item</h1></div>
                <h2>Title</h2>
                <form>
                <input
              type="text"
              placeholder="Hello World"
              name="name"
              value={title}
              onChange={onTitleChanged}
            ></input>
            <h2>Content</h2>
            <textarea
              placeholder="Content"
              name="content"
              value={content}
              onChange={onContentChanged}
            ></textarea>
                
                    <button onClick={onSavePostClicked}>SAVE</button></form>
                
            </div>
        </div>
    )
}

export default EditModal

mainscreen:

import React, { useState, useEffect } from "react";
import "../_assets/App.css";
import "../_assets/mainscreen.css";
import { MdDeleteForever } from "react-icons/md";
import { FiEdit } from "react-icons/fi";
import { useSelector } from 'react-redux';
import { useDispatch } from 'react-redux';
import { Navigate } from 'react-router-dom';
import { addPost } from '../redux/postsslice'
import {nanoid} from 'nanoid'

import Modal from "../components/modal.jsx";
import EditModal from '../components/editmodal.jsx';

function MainScreen() {

  const dispatch = useDispatch();

  const user = useSelector((state) => state.user)
const posts = useSelector((state) => state.loadPosts)

  const [title, setTitle] = useState("");
  const [content, setContent] = useState("");
  const [buttonGreyOut, setButtonGreyOut] = useState("#cccccc");

  useEffect(() => {
    if (title && content !== "") {
      setButtonGreyOut("black");
    } else {
      setButtonGreyOut("#cccccc");
    }
  },[title, content]);

  const handleSubmitSendPost = (e) => {
    e.preventDefault();
    dispatch(
      addPost({
        id: nanoid(),
        title,
        content
      })
    )
    setTitle('')
    setContent('')
  };

  const handleChangeTitle = (text) => {
    setTitle(text);
  };

  const handleChangeContent = (text) => {
    setContent(text);
  };

  const [openEditModal, setOpenEditModal] = useState();
  const [openModal, setOpenModal] = useState();

  if (user === '') {
    return <Navigate to="/" />
  } else {
    return (
      <div className="containerMainScreen">
        {openModal && <Modal closeModal={setOpenModal} />}
        {openEditModal && <EditModal closeModal={setOpenEditModal} />}
        <div className="bar">
          <h1>Codeleap</h1>
        </div>
        <div className="boxPost">
          <h2 style={{ fontWeight: 700 }}>What's on your mind?</h2>
          <h2>Title</h2>
          <form onSubmit={handleSubmitSendPost}>
            <input
              type="text"
              placeholder="Hello World"
              name="name"
              value={title}
              onChange={(e) => handleChangeTitle(e.target.value)}
            ></input>
            <h2>Content</h2>
            <textarea
              placeholder="Content"
              name="content"
              value={content}
              onChange={(e) => handleChangeContent(e.target.value)}
            ></textarea>
            <button
              className="createButton"
              type="submit"
              style={{ backgroundColor: buttonGreyOut }}
              disabled={!title || !content}
            >
              CREATE
            </button>
          </form>
        </div>
  
        {posts.map((post) => (
          <div className="boxPost" key={post.id}>
            <div className="bar">
              <h1>{post.title}</h1>
              <MdDeleteForever
                className="icon"
                onClick={() => {
                  setOpenModal(true);
                }}
              />
              <FiEdit
                onClick={() => {
                  setOpenEditModal(true);
                }}
                style={{ color: "white", fontSize: "45px", paddingLeft: "23px" }}
              />
            </div>
            <div id="postowner">
                <h3>@{user}</h3>
              <br></br>
              <textarea style={{ border: "none" }}>{post.content}</textarea>
            </div>
          </div>
        ))}
      </div>
    );
  }
  
  
  }export default MainScreen;

postSlice:

import { createSlice } from "@reduxjs/toolkit";

const postsSlice = createSlice({
  name: "posts",
  initialState: [],
  reducers: {
    addPost (state, action) {
      state.push(action.payload); // modifies the draft state.
    },
  editPost(state, action) {
    const { id, title, content } = action.payload;
    const existingPost = state.find((post) => post.id === id);
    if (existingPost) {
      existingPost.title = title
      existingPost.content = content
    }
  }
}
});

export const { addPost, editPost } = postsSlice.actions

export default postsSlice

editModal.js (edit page)

import React, {useState} from 'react';
import '../_assets/modal.css';
import { useDispatch, useSelector } from 'react-redux';
import { useLocation, useNavigate } from 'react-router-dom'

import {editPost} from '../redux/postsslice';

export function EditModal({ closeModal}) {

    const { pathname } = useLocation();
    const postId = parseInt(pathname.replace("edit-post/", ""))
    const post = useSelector((state) => state.loadPosts.find((post) => post.id === postId))
    const dispatch = useDispatch()
    const navigation = useNavigate();

    

    const [title, setTitle] = useState(post.title)
    const [content, setContent] = useState(post.content)

    

    const onTitleChanged = e => setTitle(e.target.value)
    const onContentChanged = e => setContent(e.target.value)

    const onSavePostClicked = (e) => {
        if (title && content) {
            dispatch(editPost({id: postId, title, content}))
        }
           
    }


    return (
        <div className="modalBackground">
            <div className="modalContainer">
                <div className="title"><h1>Edit item</h1></div>
                <h2>Title</h2>
                <form>
                <input
              type="text"
              placeholder="Hello World"
              name="name"
              value={title}
              onChange={onTitleChanged}
            ></input>
            <h2>Content</h2>
            <textarea
              placeholder="Content"
              name="content"
              value={content}
              onChange={onContentChanged}
            ></textarea>
                
                    <button onClick={onSavePostClicked}>SAVE</button></form>
                
            </div>
        </div>
    )
}

export default EditModal

store.js:

 import { configureStore } from '@reduxjs/toolkit';
  import userSlice from './userslice';
  import postsSlice from './postsslice'

const store = configureStore({
    reducer: {
        user: userSlice.reducer,
        loadPosts: postsSlice.reducer

    },
  })

  export default store

signup page:

import React, {useState, useEffect} from "react";
import "../_assets/signup.css";
import "../_assets/App.css";
import { useDispatch } from 'react-redux';
import userSlice from '../redux/userslice';
import { useNavigate } from "react-router-dom";

function Signup() {

  const navigate = useNavigate();

  const dispatch = useDispatch();

  const [name, setName] = useState('')

  const [buttonGrey, setButtonGrey] = useState('#cccccc')



  useEffect(() => {


      if (name!== '') {
          
          setButtonGrey("black")
      }  

      else {
        setButtonGrey('#cccccc')
        
      }
  }, [name])

  

  const handleSubmitForm= (e) => {
    e.preventDefault()
    dispatch(userSlice.actions.saveUser(name))
    navigate("/main")
  }
  
  const handleChangeName = (text) => {
    setName(text)
  }

  return (
    <div className="container">
      <div className="LoginBox">
      <form onSubmit={handleSubmitForm}>
        <h2>Welcome to codeleap network</h2>
        <text>Please enter your username</text>
        <input type="text" name="name" value={name} onChange = {e => handleChangeName(e.target.value)}  placeholder="Jane Doe"  />
        <div className="button">
        <button type="submit" style={{backgroundColor: buttonGrey}}  disabled={!name} >
          ENTER
        </button>

        </div>
        </form>
      </div>
    </div>
  );
}

export default Signup;
like image 574
Heitor Kenzou Avatar asked Jan 26 '26 20:01

Heitor Kenzou


1 Answers

With the state shape:

const store = configureStore({
  reducer: {
    user: userSlice.reducer,
    loadPosts: postsSlice.reducer

  },
});

Then the path to the posts state is state.loadPosts, so the selector in EditModal should be:

const post  = useSelector((state) => state.loadPosts.find(
  (post) => post.id === postId),
);

The error TypeError: Cannot read properties of undefined (reading 'title') is closely related to this line of code. If state.loadPosts is an empty array or there are not matching posts by id, then .find returns undefined.

const [title, setTitle] = useState(post.title); // <-- throws error on undefined post
const [content, setContent] = useState(post.content); // <-- throws error on undefined post

A quick fix could be to use the Optional Chaining operator

const [title, setTitle] = useState(post?.title);
const [content, setContent] = useState(post?.content);

but this only sets the initial state. If there is no matching post to edit then there's no point in rendering the editing UI. At this point you should render some fallback UI or navigate back, etc...

like image 155
Drew Reese Avatar answered Jan 29 '26 13:01

Drew Reese



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!