我用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;
型
1条答案
按热度按时间yptwkmov1#
使用状态形状:
字符串
那么到posts状态的路径是
state.loadPosts
,所以EditModal
中的选择器应该是:型
错误
TypeError: Cannot read properties of undefined (reading 'title')
与这行代码密切相关。如果state.loadPosts
是空数组 * 或 *id
没有匹配的帖子,则.find
返回undefined。型
一个快速解决方法是使用Optional Chaining运算符
型
但这只是设置初始状态。如果没有匹配的文章要编辑,那么渲染编辑UI就没有意义了。此时,你应该渲染一些回退UI或导航回来,等等。