项目总结如下:
- 用create-react-app创建项目结构
- 页面组件划分方式:根据UI区域划分不同的组件方便组件在不同的页面上的复用
- 组件开发
- 如何使用fetch模拟从服务器端获取数据的过程
//在页面完成挂载时获取数据
componentDidMount() {
fetch('/mock/orders.json').then(res => {
if (res.ok) {
res.json().then(data => {
this.setState({
data
})
})
}
})
};
在敲代码时遇到的坑:
1、再为评分的几颗星添加类名时 两个类名之间是需要添加空格的 在React中 添加空格可以这样 {“ ”}
所以我的解决办法就是
return <span className={"orderItem__stars" +" "+ lightClass} onClick={this.handleClickStars.bind(this,item)} key={index}>★</span>
2、添加图片时 如果没有src添加具体的图片 在浏览器中预览的时候是错位的 可能只是我个人原因 在网上查资料时 是说会有图片占位符的 但我的没有显示
3、
return item.id == id ? {
...item, comment, stars, ifCommented: true
} : item;
第二行item前面的三个点是数组扩展运算符
https://blog.csdn.net/qq_24892029/article/details/79215133 这个博主有详细介绍
项目的框架如下
项目涉及的具体代码如下
orders.json
[
{
"id":1,
"shop":"院落创意菜",
"picture":"../img/timg.jpeg",
"product":"百香果(冷饮)1扎",
"price":19.9,
"ifCommented":false
}
,{
"id":2,
"shop":"院落创意菜",
"picture":"../img/timg.jpeg",
"product":"百香果(冷饮)1扎",
"price":29.9,
"ifCommented":false
},
{
"id":3,
"shop":"院落创意菜",
"picture":"../img/timg.jpeg",
"product":"百香果(冷饮)1扎",
"price":39.9,
"ifCommented":false
},
{
"id":4,
"shop":"院落创意菜",
"picture":"../img/timg.jpeg",
"product":"百香果(冷饮)1扎",
"price":49.9,
"ifCommented":false
}
]
App__index.js
import React, {Component} from 'react';
import OrderList from '../OrderList';
import './style.css';
import Header from '../Header'
class App extends Component {
render() {
return (
<div className="app">
<Header/>
<OrderList/>
</div>
);
}
}
export default App;
Header__index.js
import React,{Component} from 'react';`在这里插入代码片`
import './style.css'
class Header extends Component{
render(){
return (
<header className='header'>
我的订单
</header>
);
}
}
export default Header;
Header__style.css
.header{
background-color: red;
color: white;
padding: 15px 10px;
text-align: center;
font-size: 16px;
line-height: 1;
}
OrderItem__index.js
import React, {Component} from 'react';
import './style.css'
class OrderItem extends Component {
constructor(props) {
super(props);
this.state = {
editing: false,
stars: props.data.stars || 0,
comment:props.data.comment ||"",
};
};
render() {
//从父组件(OrderList)获取数据
const {shop, product, price, picture, ifCommented} = this.props.data;
return (
<div className='orderItem'>
{/*
此处采用BEM命名标准 B指的是Block 也就是块 E 指的是element 一个element必须是在一个块中用两道下划线表示归属 M 指的是model 表示的是这个元素的状态 用两道上划线表示
用 BEM 命名 即表现了标签的层级关系 又表现了标签的语义信息
*/}
<div className='orderItem__picContainer'>
<img className='orderItem__pic' src={picture}/>
</div>
<div className='orderItem__content'>
<div className='orderItem__product'>{product}</div>
<div className='orderItem__shop'>{shop}</div>
<div className='orderItem__detail'>
<div className='orderItem__price'>{price}</div>
<div>
{
ifCommented ? (
<button className='orderItem__btn orderItem__btn--grey'>已评价</button>
) : (
<button className='orderItem__btn orderItem__btn--red' onClick={this.handleOpenEditArea}>评价</button>
)
}
</div>
</div>
</div>
{this.state.editing ? this.renderEditArea() : null}
</div>
);
};
//在下面的方法中绘制用于评价方法的UI
renderEditArea() {
return (
<div className='orderItem__commentContainer'>
<textarea onChange={this.handleCommentChange} value={this.state.comment} className='orderItem__comment'></textarea>
{this.renderStars()}
<button className='orderItem__btn orderItem__btn--red' onClick={this.handleSubmitComment}>提交</button>
<button className='orderItem__btn orderItem__btn--grey' onClick={this.handleCancelComment}>取消</button>
</div>
)
};
renderStars() {
const {stars} = this.state;
return (
<div>
{
[1, 2, 3, 4, 5].map((item, index) => {
const lightClass = stars >= item ?
"orderItem__stars--light" : ""
return <span className={"orderItem__stars" +" "+ lightClass} onClick={this.handleClickStars.bind(this,item)} key={index}>★</span>
})
}
</div>
)
};
//用ES6的箭头函数来命名所有的事件处理函数 这样可以保证函数内部的this指向的始终是当前组件的实例
handleOpenEditArea=()=>{
this.setState({
editing:true
})
};
handleCommentChange=(event) =>{
this.setState({
comment:event.target.value
})
};
handleClickStars=( stars ) =>{
this.setState({
stars : stars
});
};
handleCancelComment=()=>{
this.setState({
editing:false,
comment:this.props.data.comment||"",
stars: this.props.data.stars || 0,
})
};
handleSubmitComment=()=>{
const {id} = this.props.data;
const {comment , stars} = this.state;
this.setState({
editing:false,
})
//将数据提交给服务器
this.props.onSubmit(id,comment,stars)
}
}
export default OrderItem;
OrderItem__style.css
.orderItem{
display: block;
padding: 11px 10px 11px 15px;
}
.orderItem__picContainer{
padding-right: 10px;
display: inline-block;
width: 90px;
height: 90px;
}
.orderItem__pic{
width: 90px;
height: 90px;
border: 1px solid black;
}
.orderItem__content{
display: inline-block;
width: calc(100% - 100px);
}
.orderItem__product{
max-width: 237px;
font-size: 17px;
font-weight: 700;
color: #111;
padding-right: 8px;
box-sizing: border-box;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.orderItem__shop{
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-size: 13px;
color: #777;
padding-top: 9px;
padding-bottom: 13px;
}
.orderItem__detail{
display: flex;
justify-content: space-between;
height: 22px;
line-height: 22px;
padding-right:4px;
}
.orderItem__price{
font-size: 13px;
color: #777;
vertical-align: 1px;
margin-left: 10px;
}
.orderItem__price::before{
content: "\A5";
margin-right: 1px;
}
.orderItem__btn{
width: 80px;
height:22px;
text-align: center;
color: white;
border: 0;
font-size: 14px;
}
.orderItem__btn--red{
background-color: red;
}
.orderItem__btn--grey{
background-color: grey;
}
.orderItem__commentContainer{
width: 100%;
}
.orderItem__comment{
width: 100%;
height: 80px;
border: 1px solid white;;
box-sizing: border-box;
margin-top: 10px;
}
.orderItem__startContainer{
margin-bottom: 5px;
}
.orderItem__stars{
color: #999;
font-size: 20px;
}
.orderItem__stars--light{
color: rgb(233,32,61);
font-size: 20px;
}
OrderList__index.js
import React, {Component} from 'react';
import OrderItem from '../OrderItem';
class OrderList extends Component {
//在构造函数中能定义一个初始的State值
constructor(props) {
super(props);
this.state = {data: []};
};
//在页面完成挂载时获取数据
componentDidMount() {
fetch('/mock/orders.json').then(res => {
if (res.ok) {
res.json().then(data => {
this.setState({
data
})
})
}
})
};
render() {
return (
<div>
{
//利用ES6的map 语法来渲染列表项 在渲染列表项时React规定每一个列表项都得有唯一一个Key值
this.state.data.map(item => {
return <OrderItem key={item.id} data={item} onSubmit={this.handleSubmit}/>
})
}
</div>
);
}
handleSubmit = (id, comment, stars) => {
/*在实际操作中应该先将评论数据保存在后台服务器中在进行如下操作
* 保存在后台服务器的操作为
* fetch('/saveComment').then(()=>{
})
* */
const newData = this.state.data.map(item => {
return item.id == id ? {
...item, comment, stars, ifCommented: true
} : item;
});
this.setState({
data: newData
});
}
}
export default OrderList;