React中父子組件通信


本篇文章我們來學習組件之間的通信,繼續以上篇文章中提到的 添加並展示商品列表 為案例,將其功能復雜化為一個小購物車案例,即拆分一個購物車列表的子組件,跟購物車父組件進行通信。

父組件傳值給子組件,通過 props 傳值

1.在 src -> components 文件夾中,新建子組件 Cart.js ,我們創建基於類的子組件,父組件傳過來的值通過 this.props 調用中,具體代碼如下:

import React, { Component } from 'react';
// 基於類的組件
export default class Cart extends Component {
    render() {
        return (
            <table>
                <tbody>
                    {this.props.list.map(item  => (
                        <tr key={item.id}>
                            <td>名稱:{item.name}</td>
                            <td>數量:{item.count}</td>
                        </tr>
                    ))} 
                </tbody>
            </table>
        );
    }
}

上面的代碼中我們給出的是基於類的組件的傳值方式,如果你更習慣於函數類型的組件創建,那在函數中直接使用屬性傳過來的值即可,具體代碼如下:

import React, { Component } from 'react';
//  函數類型的組件
export function Cart({list}){
    return (
        <table>
            <tbody>
                {list.map(item  => (
                <tr key={item.id}>
                    <td>名稱:{item.name}</td>
                    <td>數量:{item.count}</td>
                </tr>
                ))} 
            </tbody>
        </table> 
    )
}

2.打開 src -> components -> CartSample.js 文件,導入上一步創建的 Cart 組件,並通過 props 傳值,同時在商品列后新增 加購 按鈕,點擊實現加入購物車效果,並展示在頁面,具體代碼實現如下:

import React, { Component } from 'react';
// 基於類的組件 導入方式
import Cart from './Cart';
// 函數類型的組件 導入方式
// import { Cart } from './Cart';

export default class CartSample extends Component {
    //  狀態的初始化一般放在構造器中
    constructor(props){
        super(props);
        this.state = {
            text: '',
            goods: [],
            cartList: []
        }
        this.textChange = this.textChange.bind(this)
    }
    // 當 input 的值 text 發生變化的時候,我們讓 textChang 去切換 input 的值
    textChange (event){
        this.setState({text: event.target.value})
    }
    addGoods = () => {
        this.setState(prevstate => {
            // react 官方希望傳入與返回的對象不應該是同一個對象
            return {
                goods: [
                    ...prevstate.goods,
                    {
                        id: prevstate.goods.length + 1,
                        name: prevstate.text
                    }
                ]
            }
        })
    }
    // 加購
    addToCart = (good) => {
        // 創建一個新的購物車列表
        const newCartList = [ ...this.state.cartList ]
        // 根據 商品ID 判斷購物車內有無傳入的商品
        const idx = newCartList.findIndex( c => c.id === good.id)
        // 找到該 idx 對應的商品 
        const item = newCartList[idx]
        if(item){
            // 如果購物車內有該商品,則商品數量 +1
            // 刪除 idx 項,再新增一項,新增項的屬性和 item 一樣,唯獨修改 count
            newCartList.splice(idx,1,{ ...item,count: item.count +1 })
        }else{
            // 如果購物車內沒有該商品
            newCartList.push({...good,count:1})
        }
        //更新
        this.setState({cartList:newCartList})
    }
    render() {
        return (
            <div>
                {/* 事件處理 */}
                <div>
                    <input type="text" value={this.state.text} onChange={this.textChange}/> 
                    <button onClick={this.addGoods}>加入商品</button>
                </div>
                <h1>商品列表</h1>
                <ul>
                   {this.state.goods.map((good) => (
                       <li key={good.id}> 
                            {good.name} 
                            {/* 新增 加購按鈕 */}
                            <button onClick={() => this.addToCart(good)}>加入購物車</button>
                        </li>
                   ))}
                </ul>
                {/* 購物車 */}
                <h1>購物車列表</h1>
                 {/* 通過 list 屬性吧購物車列表傳給 cart 組件 */}
                <Cart list={this.state.cartList}></Cart>
            </div>
        );
    }
}

子組件傳值給父組件

React 中子組件想傳值給父組件,是父組件把回調函數直接通過 屬性的方式傳給子組件,稱之為狀態提升,即把子組件的狀態提升出來,派發到父組件上,告訴子組件數據發生變化時應該調用哪個函數,那要怎么實現呢?
1.繼續以上面的代碼為例,新增子組件中商品的數量可以加減的需求,那就要求我們把修改子組件 Cart.js 的代碼,在數量 count 標簽前后新增 -+ 的按鈕,把原先 tr 標簽中的代碼改成如下:

<td>名稱:{item.name}</td>
<td>數量:
    <button onClick={() => minus(item)}>-</button>
        {item.count}
    <button onClick={() => add(item)}>+</button>
</td>

2.打開 src -> components -> CartSample.js 文件,在原有代碼的基礎上修改以下代碼:

// 新增函數,處理數量的更新
add = (good) =>{
    const newCartList = [ ...this.state.cartList ]
    const idx = newCartList.findIndex( c => c.id === good.id)
    const item = newCartList[idx]
    // 點擊 + 按鈕其實就是購物車內有該商品,然后該商品數量 +1
    newCartList.splice(idx,1,{ ...item,count: item.count +1 })
    this.setState({cartList:newCartList})
}
minus = (good) =>{
    const newCartList = [ ...this.state.cartList ]
    const idx = newCartList.findIndex( c => c.id === good.id)
    const item = newCartList[idx]
    // 點擊 + 按鈕其實就是購物車內有該商品,然后該商品數量 -1
    newCartList.splice(idx,1,{ ...item,count: item.count -1 })
    this.setState({cartList:newCartList})
}

{/* 原本 cart 標簽新增 add 和 minus 屬性 */}
<Cart list={this.state.cartList} add={this.add} minus={this.minus}></Cart>

其實上面的重復代碼有很多,是可以優化的,下面我們來做一下優化:

// Cart.js 中修改 + - 按鈕的點擊事件
<td>數量:
    <button onClick={() => this.props.countChange(item)}>-</button>
        {item.count}
    <button onClick={() => this.props.countChange(item,1)}>+</button>
</td>

// CartSample.js 中刪除 add 和 minus 函數,新增 countChange
countChange = (good,type) =>{
    const newCartList = [ ...this.state.cartList ]
    const idx = newCartList.findIndex( c => c.id === good.id)
    const item = newCartList[idx]
    // 購物車列表中肯定有該商品,因為不需要判斷 item 是否存在,只需要對 count 進行操作即可 
    if(type){// 有 type 即是 點擊 + 按鈕,商品數量 +1
        newCartList.splice(idx,1,{ ...item,count: item.count +1 })
    }else{ // 否則就是點擊 - 按鈕,商品數量 -1
        newCartList.splice(idx,1,{ ...item,count: item.count -1 })
    }
    this.setState({cartList:newCartList})
}
{/* 刪除 add 和 minus 函數,新增 countChange 的屬性*/}
<Cart list={this.state.cartList} countChange={this.countChange}></Cart>


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM