ReactDOM 只渲染一个组件

时间:2021-05-25 17:16:56

标签: javascript node.js reactjs next.js

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

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

我正在使用 MySQL。用于 HTTP 请求的 Axios。以及用于框架的 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

但它只呈现这个:

enter image description here

预期:

enter image description here

2 个答案:

答案 0 :(得分:0)

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

像这样:

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 的内容。因此,您必须创建一个新元素,将其附加到 commentListRef,并将 react 组件安装到该元素上:


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

答案 1 :(得分:0)

ReactDOM.render 只会为给定的容器渲染一个组件。来自the docs

<块引用>

第一次调用时,任何现有的 DOM 元素都会被替换。之后的调用使用 React 的 DOM diffing 算法进行高效更新。

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

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

相反,您可以将数据存储在 CommentsList 组件的状态中,然后从父级的 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>
        )
    }
}

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

此外,我建议您转向 React Hooks 而不是基于类的组件——一旦您掌握了钩子,就会更容易使用。