我目前正在完成一项前端导师挑战,包括建立一个互动 comments 区.在这个项目中,我希望用户能够不断地相互回复,因此我使用了一个递归组件.

我将粘贴这两个有问题的组件的代码

下面是我的注释组件代码

// Comment.jsx
import { useEffect } from "react";
import ReplySection from "./ReplySection";

const Comment =({content, upvotes,image,username, isUser, replies, replyMethod, createdAt, id,upvoteMethod})=> {
   useEffect(()=> {
       console.log(replies)
   })
    return(
        
        <div className="comment-container" key = {id}>
            <div className="comment">
                <div className="upvotes-section">
                    <div className="upvotes">
                        <img id="upvote" src="/images/icon-plus.svg" onClick={()=> {
                            upvoteMethod(id, "upvote")
                        }}></img>
                        <h3>{upvotes}</h3>
                        <img id="downvote" src="/images/icon-minus.svg" onClick={()=> {
                            upvoteMethod(id, "downvote")

                        }}></img>

                    </div>
                </div>
                <div className="comment-side">
                    <div className="comment-header">
                        <div className="profile">
                            <img src={image}></img>
                            <h5>{username}</h5>
                            <h6 className="created-at">{createdAt}</h6>
                        </div>
                        <div className="options">
                            <img src={isUser ? "images/icon-delete.svg" : ""}></img>
                            <img src="/images/icon-reply.svg" onClick={()=> {
                                replyMethod(id)
                            }}></img>
                        </div>
                    </div>

                    <div className="comment-content">
                        <h5>{content}</h5>
                    </div>
                </div>
            
            </div>

            <ReplySection reply={replies} replyMethod={replyMethod} upvoteMethod={upvoteMethod}></ReplySection>

          
        </div>
    )

}

export default Comment;

这里是回复部分,每个 comments 的所有回复都存储在这里.

import { useEffect } from "react";
import Comment from "./Comment";
const ReplySection=({reply,replyMethod,upvoteMethod})=> {
    return(
        <div className="reply-section">
           {reply.map(el=> {
               return(
                 <Comment content = {el.content} upvotes={el.score} id={el.id} upvoteMethod = {upvoteMethod} image = {el.user.image.png} username = {el.user.username} replyMethod = {replyMethod} replies = {el.replies} createdAt={el.createdAt} isUser={ el.user.username === "juliusomo"? true :false} key={el.id}></Comment>
                  
               )
           })}
        </div>
    )

}

export default ReplySection;

这是我的App jsx文件,我传递的对象和方法就在这里.

import Comment from './components/Comment';
import './App.css';
import CommentArea from './components/CommentArea';
import TextField from './components/TextField';
import { useState } from 'react';

function App() {
  let date = new Date()
  let displayedComments = [
    {
      "id": 1,
      "content": "Impressive! Though it seems the drag feature could be improved. But overall it looks incredible. You've nailed the design and the responsiveness at various breakpoints works really well.",
      "createdAt": "1 month ago",
      "score": 12,
      "user": {
        "image": { 
          "png": "./images/avatars/image-amyrobson.png",
          "webp": "./images/avatars/image-amyrobson.webp"
        },
        "username": "amyrobson"
      },
      "replies": []
    },
    {
      "id": 2,
      "content": "Woah, your project looks awesome! How long have you been coding for? I'm still new, but think I want to dive into React as well soon. Perhaps you can give me an insight on where I can learn React? Thanks!",
      "createdAt": "2 weeks ago",
      "score": 5,
      "user": {
        "image": { 
          "png": "./images/avatars/image-maxblagun.png",
          "webp": "./images/avatars/image-maxblagun.webp"
        },
        "username": "maxblagun"
      },
      "replies": [
        {
          "id": 3,
          "content": "If you're still new, I'd recommend focusing on the fundamentals of HTML, CSS, and JS before considering React. It's very tempting to jump ahead but lay a solid foundation first.",
          "createdAt": "1 week ago",
          "score": 4,
          "replyingTo": "maxblagun",
          "user": {
            "image": { 
              "png": "./images/avatars/image-ramsesmiron.png",
              "webp": "./images/avatars/image-ramsesmiron.webp"
            },
            "username": "ramsesmiron"
          }
        },
        {
          "id": 4,
          "content": "I couldn't agree more with this. Everything moves so fast and it always seems like everyone knows the newest library/framework. But the fundamentals are what stay constant.",
          "createdAt": "2 days ago",
          "score": 2,
          "replyingTo": "ramsesmiron",
          "user": {
            "image": { 
              "png": "./images/avatars/image-juliusomo.png",
              "webp": "./images/avatars/image-juliusomo.webp"
            },
            "username": "juliusomo"
          }
        }
      ]
    }
  ]


  const [comments,setComment] = useState(displayedComments);

  const upvotePost=(id, action)=> {

    let counter = 0
    action==="upvote"? counter++ : counter--;

    if (Math.abs(counter) <= 1) {
      const mult = action === "upvote" ? 1 : -1;
      setComment(
         comments => comments.map((comment) =>comment.id === id
          ? {...comment, score: comment.score + (1*mult)}
          : comment
        )
      ); 
    }  
    console.log(counter)  
  }
   
  
  
  const dateToMonth=(month)=> {
    const months = ["January","February","March","April","May","June","July","August","September","October","November","December"]

    return months[month]
  }
  const newComment=()=> {
    let comment = document.querySelector(".text-area")
    if (comment.value !="") {
      setComment([...comments, {"id":Math.random()*100000,
      "content":comment.value,
      "createdAt": `${dateToMonth(date.getMonth())}`,
      "score": 0,
      "user": {
        "image": { 
          "png": "./images/avatars/image-juliusomo.png",
          "webp": "./images/avatars/image-juliusomo.webp"
        },
        "username": "juliusomo"
      },
      "replies": []}])
    }
   

  } 

  const newReply=(id)=> {
    let commentClone = [...comments]
    for (let i =0; i<commentClone.length; i++) {
      if (commentClone[i].id == id) {
        commentClone[i].replies.push({
          "id": Math.random() * 10000,
          "content": "I couldn't agree more with this. Everything moves so fast and it always seems like everyone knows the newest library/framework. But the fundamentals are what stay constant.",
          "createdAt": "2 days ago",
          "score": 2,
          "replyingTo": "ramsesmiron",
          "user": {
            "image": { 
              "png": "./images/avatars/image-juliusomo.png",
              "webp": "./images/avatars/image-juliusomo.webp"
            },
            "username": "juliusomo"
          }
        })
        setComment(commentClone)
      }
    } 
  }
  return (
    <div className="App">
      <CommentArea  replyMethod = {newReply} upvoteMethod ={upvotePost} comments={comments}></CommentArea>
      <TextField commentMethod={newComment}></TextField>
       
    </div>
  );
}

export default App;

当我打开live server时,我最终遇到了以下错误:

Uncaught TypeError: Cannot read properties of undefined (reading 'map')
    at ReplySection (ReplySection.jsx:5:1)
    at renderWithHooks (react-dom.development.js:16398:1)
    at mountIndeterminateComponent (react-dom.development.js:21144:1)
    at beginWork (react-dom.development.js:22634:1)
    at beginWork$1 (react-dom.development.js:27597:1)
    at performUnitOfWork (react-dom.development.js:26733:1)
    at workLoopSync (react-dom.development.js:26642:1)
    at renderRootSync (react-dom.development.js:26611:1)
    at recoverFromConcurrentError (react-dom.development.js:26020:1)
    at performSyncWorkOnRoot (react-dom.development.js:26264:1)

奇怪的是,当我在我的 comments 组件中记录回复数组时,它只显示了一个数组,没有任何异常.

我被这个问题难住了,想知道发生了什么事.非常感谢您的帮助.

推荐答案

导致这种情况的情况是...


第一次使用Comment时,replies状态被初始化为undefined.稍后可能会在effect hook中进行更新,但在此之前,您无法调用map().

简单的解决方案是将数组状态初始化为数组

const [replies, setReplies] = useState([]); // empty array

有些嵌套的replies没有replies个属性,即el.replies未定义.

在这种情况下,你可以使用optional chaining安全地拨打.map()...

{reply?.map((el) => (
  <Comment
    content={el.content}
    ... etc
  />
))}

或者如果你需要更多的布局控制,使用conditional rendering

{reply && (
  {/* content when reply exists */}
)}
{!reply && (
  {/* content when reply does not exist */}
)}

Javascript相关问答推荐

TypScript界面中的Infer React子props

colored颜色 检测JS,平均图像 colored颜色 检测JS

Chromium会将URL与JS一起传递到V8吗?

在286之后恢复轮询

可更改语言的搜索栏

使用ThreeJ渲染的形状具有抖动/模糊的边缘

在forEach循环中获取目标而不是父对象的属性

使用Nuxt Apollo在Piniastore 中获取产品细节

一个实体一刀VS每个实体多刀S

如何防止ionic 输入中的特殊字符.?

未捕获的运行时错误:调度程序为空

我想使用GAS和HTML将从Electron 表格中获得的信息插入到文本字段的初始值中

FireBase云函数-函数外部的ENV变量

在D3条形图中对具有相同X值的多条记录进行分组

OnClick更改Json数组JSX中的图像源

Reaction即使在重新呈现后也会在方法内部保留局部值

在Press Reaction本机和EXPO av上播放单个文件

脚本语法错误只是一个字符串,而不是一个对象?

JSON Web令牌(JWT)错误:RSA密钥对的签名无效

JQuery-无法 Select 使用elementor添加的元素的值