繁体   English   中英

ReactDOM 只渲染一个组件

[英]ReactDOM renders only one component

我想创建一个具有评论功能的应用程序。 我正在尝试这样的代码:

response.data.forEach((el, idx, arr) => {
  const newMessage = <CommentMessage username={el.username} message={el.message}/>
  ReactDOM.render(newMessage, this.commentListRef.current)
})

我正在使用 MySQL。 Axios 用于 HTTP 请求。 而Next.js为框架。

完整代码:

import React from 'react'
import ReactDOM from 'react-dom'
import styles from './comments-list.module.css'

class CommentMessage extends React.Component {
    constructor(props) {
        super(props)
    }
    render() {
        return (<div>
            <b>{this.props.username}</b>
            <span>: </span>
            <span>{this.props.message}</span>
        </div>)
    }
}

class CommentsList extends React.Component {
    constructor(props) {
        super(props)

        this.commentListRef = React.createRef()

        const comments = []
    }
    loadComments() {
        const axios = require('axios')
        axios.get('/api/getcomments')
            .then(response => {
                response.data.forEach((el, idx, arr) => {
                    const newMessage = <CommentMessage username={el.username} message={el.message}/>
                    ReactDOM.render(newMessage, this.commentListRef.current)
                })
            })
            .catch(err => {
                console.log(err)
            })
    }
    render() {
        return (<div ref={this.commentListRef} onLoad={this.loadComments()}>

        </div>)
    }
}

export default CommentsList

但它只呈现这个:

在此处输入图像描述

期待这个:

在此处输入图像描述

你的做法很奇怪; 我不知道这是不是故意的。 无论如何,推荐的方法是将评论存储为组件的 state 的一部分,并在收到评论时更新 state。

像这样:

class CommentsList extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
          comments: []
        };

        this.commentListRef = React.createRef()

        const comments = []
    }
    loadComments() {
        const axios = require('axios')
        axios.get('/api/getcomments')
            .then(response => {
              this.setState({
                comments: response.data
              });
            })
            .catch(err => {
                console.log(err)
            })
    }
    
    componentDidMount(){
      this.loadComments();
    }
    
    render() {
        return (<div ref={this.commentListRef}>
            (this.state.comments.map(comment => (
                <CommentMessage username={comment.username} message={comment.message}/>
            )))
        </div>)
    }
}

此外,您的 onLoad 没有按预期工作。 每次组件呈现时它都会调用 loadComments,我什至不知道 onLoad 是否是 div 上的正确事件。

无论如何,如果您绝对想按照自己的方式进行操作,则必须将每个节点安装到其自己的容器中。 正如您现在所拥有的,每条评论都覆盖了commentListRef 的内容。 因此,您必须创建一个新元素 append 到 commentListRef,并将反应组件安装到该元素:


   loadComments() {
       const axios = require('axios')
       axios.get('/api/getcomments')
           .then(response => {
               response.data.forEach((el, idx, arr) => {
                   const element = document.createElement('div');
                   this.commentListRef.current.appendChild(element);
                   const newMessage = <CommentMessage username={el.username} message={el.message}/>
                   ReactDOM.render(newMessage, element)
               })
           })
           .catch(err => {
               console.log(err)
           })
   }

ReactDOM.render只会为给定容器渲染一个组件。 文档

首次调用时,内部的任何现有 DOM 元素都会被替换。 后面的调用使用 React 的 DOM diffing 算法进行有效更新。

基本上,当您在循环中调用ReactDOM.render时,React 将每个给定组件视为对前一个组件的更新,而不是单独渲染每个组件。

最佳实践是在根容器(通常称为<App> )呈现单个组件。 但是,您似乎已经这样做了,因为这些ReactDOM.render调用正在另一个组件中发生。 通常,您应该只需要在应用程序中使用ReactDOM.render一次。

相反,您可以将数据存储在CommentsList组件的 state 中,然后从父组件的render方法中返回子组件。

例如:

class CommentsList extends React.Component {
    constructor(props) {
        super(props)
        
        this.state = {
            comments: [],
        }
    }

    loadComments = () => {
        const axios = require('axios')
        axios.get('/api/getcomments')
            .then(response => {
                this.setState(prev => ({...prev, comments: response.data}));
            })
            .catch(err => {
                console.log(err)
            })
    }
    
    render() {
        const { comments } = this.state;
        
        return (
            <React.Fragment>
                {comments.map(e => (
                    <CommentMessage key={e.id} username={e.username} message={e.message}/>
                ))}
            </React.Fragment>
        )
    }
}

注意:我还向CommentMessage组件传递了一个key ,以便为每个孩子提供一个稳定的身份(有关更多信息,请参阅文档)。 不得不猜测,但我假设评论会有一个id值,如果没有,您可以为评论选择一个不同的唯一值作为键。

此外,我建议将基于类的组件迁移到React Hooks ——一旦你掌握了钩子,使用起来会更容易。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM