React 小案例 訂單列表評價

Winter_Wang發表於2019-01-16

最終效果圖:

1.功能:各功能元件的組合和渲染

React 小案例 訂單列表評價    

2.點選評價按鈕評論元件出現,可輸入文字和打分,點選取消按鈕關閉評論

React 小案例 訂單列表評價

3.點選提交按鈕顯示已評價

React 小案例 訂單列表評價


結構:

1.在src下建立components資料夾,用於存放幾個元件,每個元件建立一個資料夾存放,

2.將檢視展示的App.js移入components資料夾下,且建立App資料夾,

3.每個元件的js檔案均命名為index,為引入方便,

4.OrderItem元件為渲染整個列表,

5.OrderList元件為單個列表內容,

6.Header為頂部紅色部分,

React 小案例 訂單列表評價

src資料夾index.js所有內容:

1.改變了App.js的位置,引入index.js時的路徑也要改變,

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './components/App/index';


ReactDOM.render(<App />, document.getElementById('root'));

複製程式碼

App資料夾下,index.js所有內容:

1.引入OrderList元件和Header元件,引入時無需新增index,index檔案自動預設渲染

2.將兩個元件檢視渲染到該渲染的位置

import React, { Component } from 'react';
import OrderList from '../OrderList'
import Header from '../Header'
import './style.css'

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 index extends Component{
    render(){
        return(
            <header className="header">
                我的訂單
            </header>
        );
    }
}

export default index複製程式碼

style.css:

.header{
    background-color: #e9203d;
    color: #fff;
    padding: 15px 10px;
    text-align: center;
    font-size: 16px;
    line-height: 1;
}複製程式碼


單個列表OrderItem中所有內容:

1.建立出單個商品列表介面,

2.判斷評價按鈕的紅色灰色狀態,在state中定義狀態:editing預設為false,

3.在標籤上定義邏輯,如果為true,那麼為已評價,class設定為灰色,如果為false,則保持預設未評價,class設定為紅色,

4.這時建立父元件OrderList,遍歷渲染所有列表,

5.做完父元件的第8項,返回子元件繫結data資料,

6.定義一個const接收父元件props過來data資料的每項資料名,將資料直接繫結在相應位置,

7.建立renderEditArea()函式,為點選評價輸入和評分部分的檢視,

將評價檢視繫結在列表下面,並定義state中editing判定,為true時顯示渲染,為false時不渲染,

{this.state.editing ? this.renderEditArea() : null}複製程式碼

8.給評價按鈕增加onclick事件,handleOpenEidt(),點選時將editing狀態改為true,評價部分渲染顯示,

9.給評價部分文字域textarea繫結onChange讓元件受控,設定文字域value值,在state中初始值設為‘’,然後繫結給textarea的value上,

在handleCommentChange()事件中,把state的value修改為使用者當下輸入,則可監聽到使用者輸入,

10.給文字域的取消按鈕繫結點選事件,在取消按鈕事件中,修改state中的editing為false,comment為‘’,取消後清空文字域文字,並讓評價部分消失,

11. 建立五角星檢視函式,renderStars(),

建立span中間放置繪製的一個五角星,建立陣列1-5遍歷span,在state中定義一個資料為stars初始值為0,遍歷中定義一個linghtClass,判斷當stars大於等於當下的五角星個數時,輸入一個新calss名,用於點亮背景色設定,如果不是則不設定,然後將linghtClass繫結在span的calssName中,

12.給span這個五角星繫結點選事件 onClick={this.handleClickStars.bind(this,item)}

因為要判斷當前點選,所以要給函式繫結item引數,

13.定義五角星點選事件,函式接收一個引數為當前點選的stars,修改state中初始stars的資料為當前點選的資料,

14.給提交按鈕繫結事件,提交時需要提交至父元件所有商品列表中,所以父元件需要接收哪條

商品的id資訊,該商品文字域內容,和打分情況,

將這些所需資訊資料,作為引數定義onSubmit提交事件的引數傳遞給父元件

15.此時去父元件接收

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() {
        const {shop, product, price, picture, ifCommented} = this.props.data;
        return (
            <div className="OrderItem">
                <div className="orderItem_picContainer">
                    <img className="orderItem_pic" src={picture} alt=""/>
                </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.handleOpenEidt}>
                                        評價</button>
                                )
                            }
                        </div>
                    </div>
                </div>
                {/*當state中editing為true時渲染*/}
                {this.state.editing ? this.renderEditArea() : null}
            </div>
        );
    }

    //評價部分輸入
    renderEditArea() {
        return (
            <div className="orderItem_commentContainer">
                <textarea onChange={this.handleCommentChange} value={this.state.comment} className="orderItem_comment"/>
                {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_star_linght' : '';
                        return (
                            <span className={"orderItem_star  "+ lightClass} key={index} onClick={this.handleClickStars.bind(this,item)}>★</span>
                        )
                    })
                }
            </div>
        )
    }
    //是否出現評價框
    handleOpenEidt =()=>{
        this.setState({
            editing :true
        })
    }
    //讓輸入框受控
    handleCommentChange =(e)=>{
        this.setState({
            comment:e.target.value
        })
    }

    //點亮五角星
    handleClickStars =(stars)=>{
        this.setState({
            stars:stars
        })
    }

    //取消按鈕
    handleCancelComment=()=>{
        this.setState({
            editing:false,
            stars: this.props.data.stars || 0,
            comment:this.props.data.comment || ''
        })
    }
    //提交按鈕
    handleSubmitComment=()=>{
        const {id} =this.props.data;
        const {comment,stars} = this.state;
        this.setState({
            editing:false,
        })
        this.props.onSubmit(id, comment, stars )
    }

}

export default OrderItem複製程式碼


父元件所有列表OrderList元件所有程式碼:

1.首先模擬一個從後端獲取的data資料,

在public資料夾中,建立mock資料夾,mock資料夾中建立orders.json檔案用於存放data資料,

3.在鉤子函式componentDidMount()中,用fetch請求獲取地址為‘/mock/orders.json’的資料,

4.在state裡初始化data,

5.在鉤子函式componentDidMount()中將state中初始化的data變為獲取到的data資料,

6.將子元件OrderItem引入,並渲染,

7.將獲取到的data資料遍歷子元件<OrderItem/>,將資料中的id作為唯一的key值,

8.將data資料傳給子元件,用於綁在子元件列表的資料相應位置,此時去子元件,

9.在父元件的子元件標籤中,接收onSubmit事件,同時定義事件函式handleSubmit(),

10.handleSubmit中接收到id,comment,stars三個引數,

定義一個newData,接收改變後的新資料,遍歷舊資料,判斷,當下id和data中的id為同一條時,將所有item接收為子元件的新值和狀態,不是當下這條id,內容不變,

11.將newData更新至data中,

import React, {Component} from 'react'
import OrderItem from '../OrderItem'


class OrderList extends Component {

    constructor(props) {
        super(props);
        this.state = {data: []};
    }

    //用獲取json資料渲染至列表
    componentDidMount() {
        //因為public檔案下的資源可以直接被獲取,所以直接寫路徑即可
        fetch('/mock/orders.json').then(res => {
            if (res.ok) {
                res.json().then(data => {
                    this.setState({
                        data
                    })
                })
            }
        })
    }

    render() {
        return (
            <div>
                {
                    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;
        });
        //將newData更新至data中
        this.setState({
            data: newData
        })
    }
}

export default OrderList複製程式碼

orders.json檔案中所有內容:

1.提取列表檢視中所有動態資料,存放在orders.json檔案中,

用id區分每條資料,

 [
  {
    "id":1,
    "shop":"肯德基",
    "picture":"http://02.imgmini.eastday.com/mobile/20180405/20180405042401_0e8c94915ba80f288e2aba04de54ce5e_1.jpeg",
    "product":"肯德基全家桶",
    "price":59.9,
    "ifCommented":false
  },
  {
    "id":2,
    "shop":"牛排王子",
    "picture":"http://www.vx001.com/uploads/allimg/170805/1-1FP510551I62.jpg",
    "product":"西冷牛排套餐",
    "price":98,
    "ifCommented":false
  },
  {
    "id":3,
    "shop":"必勝客",
    "picture":"http://imgsrc.baidu.com/imgad/pic/item/c995d143ad4bd1131a9ef8c450afa40f4afb05f0.jpg",
    "product":"匹薩",
    "price":120,
    "ifCommented":false
  },
  {
    "id":4,
    "shop":"潮人餐廳",
    "picture":"http://imgsrc.baidu.com/imgad/pic/item/0d338744ebf81a4c2956133cdd2a6059242da6db.jpg",
    "product":"雞排飯",
    "price":25,
    "ifCommented":false
  }
]複製程式碼

OrderItem中的所有css:

.OrderItem{
    display: block;
    padding:11px 10px 11px 15px;
}
.orderItem_picContainer{
    padding-right: 10px;
    display: inline-block;
}
.orderItem_pic{
    width: 90px;
    height: 90px;
}
.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: #fff;
    border:0;
    font-size: 14px;
}
.orderItem_btn_red{
    background-color: #e9203d;
}
.orderItem_btn_grey{
    background-color: #999;
}
.orderItem_comment{
    width: 100%;
    resize:none;
    margin-top: 10px;
    height: 50px;
}
.orderItem_star{
    font-size: 25px;
    cursor: pointer;
    color: #777777;
}
.orderItem_star_linght{
    color: #e9203d;
}複製程式碼


相關文章