简体   繁体   English

ReactDOM 只渲染一个组件

[英]ReactDOM renders only one component

I want to create an app with comments feature.我想创建一个具有评论功能的应用程序。 I am trying with the code like this:我正在尝试这样的代码:

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

I am using MySQL.我正在使用 MySQL。 Axios for HTTP Requests. Axios 用于 HTTP 请求。 And Next.js for the framework.而Next.js为框架。

Full code:完整代码:

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

But it only render this:但它只呈现这个:

在此处输入图像描述

Expected this:期待这个:

在此处输入图像描述

You're going about this pretty strangely;你的做法很奇怪; I don't know if that's on purpose or not.我不知道这是不是故意的。 Regardless, the recommended approach would be to store the comments as part of your component's state, and update the state when you get the comments.无论如何,推荐的方法是将评论存储为组件的 state 的一部分,并在收到评论时更新 state。

Like this:像这样:

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>)
    }
}

Also, your onLoad wasn't working as you had expected.此外,您的 onLoad 没有按预期工作。 It will call loadComments every time the component renders, and I don't even know if onLoad is a proper event on a div.每次组件呈现时它都会调用 loadComments,我什至不知道 onLoad 是否是 div 上的正确事件。

At any rate, if you absolutely wanted to do it the way you did it, you would have to mount each node into its own container.无论如何,如果您绝对想按照自己的方式进行操作,则必须将每个节点安装到其自己的容器中。 As you have it right now, each comment is overwriting the contents of commentListRef.正如您现在所拥有的,每条评论都覆盖了commentListRef 的内容。 So you'd have to create a new element, append that to commentListRef, and mount the react component to that:因此,您必须创建一个新元素 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 will only render one component for a given container. ReactDOM.render只会为给定容器渲染一个组件。 From the docs :文档

Any existing DOM elements inside are replaced when first called.首次调用时,内部的任何现有 DOM 元素都会被替换。 Later calls use React's DOM diffing algorithm for efficient updates.后面的调用使用 React 的 DOM diffing 算法进行有效更新。

Basically when you call ReactDOM.render in a loop, React is treating each given component as an update to the previous component, rather than rendering each individually.基本上,当您在循环中调用ReactDOM.render时,React 将每个给定组件视为对前一个组件的更新,而不是单独渲染每个组件。

Best practice is to render a single component at the root container (usually called <App> ).最佳实践是在根容器(通常称为<App> )呈现单个组件。 However it seems you've already done this as these ReactDOM.render calls are happening within another component.但是,您似乎已经这样做了,因为这些ReactDOM.render调用正在另一个组件中发生。 Generally, you should only need to use ReactDOM.render once within an app.通常,您应该只需要在应用程序中使用ReactDOM.render一次。

Instead you can store the data in the CommentsList component's state and just return the children components from the parent's render method.相反,您可以将数据存储在CommentsList组件的 state 中,然后从父组件的render方法中返回子组件。

For example:例如:

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>
        )
    }
}

Note: I've also passed a key to the CommentMessage component to give each child a stable identity (see docs for more info).注意:我还向CommentMessage组件传递了一个key ,以便为每个孩子提供一个稳定的身份(有关更多信息,请参阅文档)。 Had to guess, but I assume a comment would have an id value, if not, you can choose a different unique value for the comment to use as a key.不得不猜测,但我假设评论会有一个id值,如果没有,您可以为评论选择一个不同的唯一值作为键。

Also I'd recommend moving to React Hooks over class-based components—a lot easier to work with once you get a grasp on hooks.此外,我建议将基于类的组件迁移到React Hooks ——一旦你掌握了钩子,使用起来会更容易。

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

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