[英]react component renders twice with only one setState in componentWillMount
[英]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.