redux 无法读取未定义的属性(阅读“find”)

uelo1irk  于 9个月前  发布在  其他
关注(0)|答案(1)|浏览(123)

我用redux做了一个CRUD。应用程序有一个带有用户名的表单,然后有一个屏幕来创建,查看,编辑和删除帖子,我正在做编辑部分。然而,当我使用array.find()遍历数组并查找每个帖子时,我得到了这个错误:
无法读取未定义的属性(阅读“find”)
我会把密码留在下面。

编辑界面:

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

字符串

主屏幕:

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(编辑页面)

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

注册页面:

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;

yptwkmov

yptwkmov1#

使用状态形状:

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

  },
});

字符串
那么到posts状态的路径是state.loadPosts,所以EditModal中的选择器应该是:

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


错误TypeError: Cannot read properties of undefined (reading 'title')与这行代码密切相关。如果state.loadPosts是空数组 * 或 * id没有匹配的帖子,则.find返回undefined。

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


一个快速解决方法是使用Optional Chaining运算符

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


但这只是设置初始状态。如果没有匹配的文章要编辑,那么渲染编辑UI就没有意义了。此时,你应该渲染一些回退UI或导航回来,等等。

相关问题