- 目标
目的:自己做记录,记录制作过程以及遇到的问题。内容来自b站的up主:黑马程序员(up主名字)的黑马程序员黑马程序员前端React视频教程....(视频名称部分)。
效果展示:
2. 开始制作
2.1 创建项目
新建一个文件夹,用vscode打开,在vscode中打开终端,在终端内数输入“npx create-react-app comm”(comm是项目名,自己取的)
在终端输入“cd comm”,再输入“yarn start(/npm start)”
完成基础样式,点开src文件夹里面的index.js,将里面的内容替换成如下
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
// 评论列表案例
// comments: [
// { id:1, name:'jack', content:'沙发!!!' },
// { id:2, name:'andy ', content:'板凳!!!' },
// { id:3, name:'tom', content:'楼主好人!!!' }
// ]
class App extends React.Component {
render() {
return (
<div className="app">
<div>
<input className='user' type="text" placeholder="请输入评论人" />
<br />
<textarea className='content' cols="30" rows="10" placeholder="请输入评论内容" />
<br />
<button>发表评论</button>
</div>
<div className='no-comment'>暂无评论,快去评论吧~</div>
<ul>
<li>
<h3>评论人:jack</h3>
<p>评论内容:沙发!!!</p>
</li>
</ul>
</div>
)
}
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
打开src文件夹里的index.css,将里面的内容替换成如下:
.app {
width: 233px;
margin: 20px;
padding-left: 10px;
padding-top: 5px;
border: 1px solid black;
}
.user {
margin-bottom: 10px;
}
.content {
margin-bottom: 10px;
}
.no-comment {
margin-bottom: 10px;
}
更改完之后保存,打开浏览器,就是现在这样了
2.2 渲染评论列表
要渲染,首先就要有状态,所以我们先进行状态的初始化,在state中初始化评论列表数据
初始化状态后,就需要渲染数据,此时状态放在一个数组中,所以我们用map方法遍历数组,然后为每一个评论数据生成一个li元素,所以我们接下来处理下面的li元素。先将ul里面原有的li元素内容删掉。我们通过this.state.comments拿到评论列表数据,然后调用map方法进行遍历,给每个被遍历的li元素添加key属性
2.3 渲染暂无评论
如果没有数据,就要渲染暂无数据,通过判断列表数据的长度是否为0,如果为0,则是暂无评论。
我们通过条件渲染,选择渲染什么内容。首先封装一个方法,通过renderList这个方法的返回值作为渲染的内容。
然后创建这个方法,通过三元表表达式的方式进行选择,将相应内容剪切(注意是剪切,所以先前在方面下面的那两个有无评论的内容就不在了)
可以注释掉评论内容,看能否正常展示“无评论”时的效果
2.4 获取评论信息
使用受控组件方式处理表单元素。在我们的结构中一共有两个表单元素,一个“评论人”,一个“评论内容”,这两个都可以通过value的方式来获取,所以我们可以创建同一个事件处理程序来进行统一的获取。首先我们要准备一个状态
接下来把这两个状态设置为当前两个表单元素的值,然后添加name属性
然后添加事件处理程序,处理表单元素值。因为要拿到事件对象,因此先放个事件对象e,然后接下来对他们进行处理。首先拿到当前是哪一个表单,即name;然后接下来再通过setState更新状态,更新状态需要值,因此就需要value。
然后在表单元素中分别来一个change事件
点开浏览器,按F12进入调试界面,在文本框输入内容,可以看到react里的state在随之变化了,说明这两个表单元素现在都能受到受控组件的控制了。
2.5 发表评论
首先我们要给按钮绑定点击事件,然后来一个“发表评论”的事件处理程序
如何讲我们的数据添加到state中呢?此处我们选择将数据放在第一条评论之前。由于数据是添加给comments,所以我们应该先拿到comments的数据。拿到之后,通过数组的扩展运算符先拿到一个新的数据,然后再添加数据,因为我们是添加在前面,所以在“...comments”的前面添加一个对象(加对象是因为数组中的每一项都是一个对象,我们要保证加进来的数据结构与上面保持一致)
2.5.1 边界情况
此时还存在两个问题:点击“发表评论”之后文本框不会自动清空;当什么都不输入或输入空格仍能发表。针对空格可以使用非空校验。如何清空文本框的值呢?文本框的值就是状态的值,要清空文本框就是清空状态,将它们置成空字符串即可
然后就完成啦。以下为index.js完整代码:
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
class App extends React.Component {
state = {
comments: [
{ id:1, name:'jack', content:'沙发!!!' },
{ id:2, name:'andy ', content:'板凳!!!' },
{ id:3, name:'tom', content:'楼主好人!!!' }
],
// 评论人
userName:'',
// 评论内容
userContent:''
}
// 渲染评论列表
renderList() {
return this.state.comments.length === 0
? (<div className='no-comment'>暂无评论,快去评论吧~</div>)
: (<ul>
{
this.state.comments.map(item => (
<li key={item.id}>
<h3>评论人:{item.name}</h3>
<p>评论内容:{item.content}</p>
</li>
))
}
</ul>)
}
handleForm = (e) => {
this.setState({
[e.target.name]:e.target.value
})
}
addComment = () => {
if (this.state.userName.trim() === '' || this.state.userContent.trim() === '') {
alert('请输入评论人和评论内容')
return
}
const newComments = [{
id:Math.random(),
name:this.state.userName,
content:this.state.userContent
},...this.state.comments]
// 更新状态
this.setState({
comments:newComments,
userName:'',
userContent:''
})
}
render() {
return (
<div className="app">
<div>
<input className='user' type="text" placeholder="请输入评论人" value={this.state.userName} name="userName" onChange={this.handleForm} />
<br />
<textarea className='content' cols="30" rows="10" placeholder="请输入评论内容" value={this.state.userContent} name="userContent" onChange={this.handleForm} />
<br />
<button onClick={this.addComment}>发表评论</button>
</div>
{this.renderList()}
</div>
)
}
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);