21. React简易实作_购物车清单( 将下层State提升给上层元件 )

今天要解释的是: 如何将下层State提升给上层元件

但如果没有举例真的太抽象了,所以就乾脆做个功能,边进行解释吧。

实作一个购物清单功能为例


功能包含:

  • 增减商品数量
  • 计算总价

实作步骤:

  1. 制作元件(Component)
  2. 增减商品数量(useState)
  3. 使用元件(应用Prop)
  4. 计算总价(提升State)

1. 制作元件

我们先制作一个Product的元件:

// components/product.js
import React, { useState } from "react";

const Product = () => {
  
  const product = {
    name: "Apple",   // 商品名称
    price: 100,      // 价格
  }  
	return(
		<div>
			<h2>{product.name}</h2>
			<p>${product.price}</p>
            <h3>quantity:</h3>
			<div>
				<button >+</button>
				<button >-</button>
			</div>
			<hr/>
		</div>
	)	
};

export default Product

然後引入到App.js进行渲染

// app.js
import React from "react";
import Product from "./components/product";
import "./styles/styles.css";

const App = () => {
	return(
	  <div>
        <Product/>
      </div>
	)
};

export default App

目前会是一个有【商品名称】【价格】【数量】【增减按钮】的清单
https://ithelp.ithome.com.tw/upload/images/20210922/20129476OxVIVv4cTN.png

但是现在只有渲染画面,按按钮清单也不为所动,所以要开始帮他加上一些功能:

2. 增减商品数量(useState)


增加数量

预设的商品数量是0,所以这时候可以帮【数量】设useState(0):

const [ quantity, setQuantity] = useState(0);  //  quantity 预设值= 0
        ...
      <h3>quantity:{quantity}</h3>  //渲染至画面

可以看到现在的【数量】是 0:
https://ithelp.ithome.com.tw/upload/images/20210922/20129476zQeDGcxIK4.png

接着先帮"+"按钮做增加数量的功能,宣告一个increment的函式:

	const increment = () => {
		setQuantity(quantity + 1);   // 可以想成 quantity = quantity + 1
	};

然後要记得帮button绑上功能:

    <button onClick={increment}>+</button>  // 点击时执行increment(); quantity + 1

可以按按看"+",会增加数量(被我按到5了):
https://ithelp.ithome.com.tw/upload/images/20210922/20129476NgFnusdgih.png


减少数量

宣告decrement函式,在数量>0时,执行quantity - 1:

  const decrement = () => {
    if ( quantity > 0 ){
		  setQuantity(quantity - 1);    // quantity = quantity - 1
    }
  };

因为商品不能是负数,所以这里简单加了一个if的判断式。

然後帮button绑上decrement的功能:

    <button onClick={decrement}>-</button>

到这里应该可以成功减少数量了:
https://ithelp.ithome.com.tw/upload/images/20210922/20129476ulD0wsbUNG.png

3. 使用元件(应用Prop)


接着,我们要让更多商品应用同样的功能,所以要使用刚刚完成的Product元件。

架构可以想像成这样:
https://ithelp.ithome.com.tw/upload/images/20210922/20129476XdgAuGS1wd.png
App和Component有上下级关系,
App里面包住了几个Product元件,在App设定好的资料可以作为参数(Prop)传入元件(Component)。

概念上来说,component 就像是 JavaScript 的 function,它接收任意的参数(称之为「props」)并且回传描述画面的 React element。

(如果是规模更大的专案,应该不会直接把参数绑到App,可能会先做页面包住元件。
但这里是示范功能,就把架构作的简化了一点。)

先在App设定好商品资讯,传入component:

// app.js
import React from "react";
import Product from "./components/product";
import "./styles/styles.css";


const App = () => {
    // 设定商品资讯
	const products = [
		{id: 1, name: "Android", price: 150},
		{id: 2, name: "Apple", price: 170},
		{id: 3, name: "Nokia", price: 65},
	];
	
	return(
	  <div>
            // 将商品资讯作为参数(Prop)分别传入元件(Component)
			{products.map( (p) => (
				<Product 
					key={p.id}  
					name={p.name}   
					price={p.price}
					/>
			))}
    </div>
	)
};

export default App

然後记得到product.js接住传入的props:

//  components/product.js
import React, { useState } from "react";

const Product = ({key, name, price}) => {  // 用{大括号}接受prop
  ...
  return(
		<div>
			<h2>{name}</h2>   // 把product.name改成name
			<p>${price}</p>   // 把product.price改成price
            <h3>quantity:{quantity}</h3>
			<div>
				<button onClick={increment}>+</button>
				<button onClick={decrement}>-</button>
			</div>
			<hr/>
		</div>
	)	
};

export default Product

到这里可以试试看购物清单的功能:
https://ithelp.ithome.com.tw/upload/images/20210922/20129476v5bDo7yiCy.png

应该可以成功的显示商品和增减数量了!

4. 计算总价(提升State)


最後也是最重要的,计算总价。

假设我们要制作另一个元件Total显示总价,直觉上的架构可能会长这样:
https://ithelp.ithome.com.tw/upload/images/20210922/20129476xdLfCLInu2.png

  • App 有两个 child components: Total和Product。
  • totalCash计算总价,配合Product的State变化

但这个结构会产生一个问题: TotalCach要怎麽知道每个元件的state的变化?

通常来说,有一些 component 需要反映相同的资料变化。我们建议将共享的 state 提升到最靠近它们的共同 ancestor。

所以可能要改变一下结构, 可以直接把totalCash提升为元件App的State,
同时把totalCash作为props传入元件Total。

https://ithelp.ithome.com.tw/upload/images/20210922/20129476pfcbwwJg0S.png

每当两个或以上的子元件资料会互相影响,就可以透过提升State的方式,将State提升到ancestor(上级元件),再透过传入prop的方式控制资料。

  1. 在App里设置State:
// app.js
  const [ totalCash, setTotalCash ] = useState(0);  // totalCash预设值为0
  	
  const calculate = (price) => {
    setTotalCash( totalCash + price);   // totalCash =  totalCash + price
  }

总价的预设值为0,总价随着价格增减,後面会解释得比较仔细。

  1. calculate(price)会被作为prop传入component。
// app.js
{products.map( (p) => (
    <Product 
        key={p.id} 
        name={p.name} 
        price={p.price}
        onCalculate={calculate}  // 
    />
))}

calculate(price)的参数price就是被传入product component的propprice={p.price}

  1. 在增加或减少商品数量时,执行onCalculate():
// product.js
const Product = ({key, name, price, onCalculate}) => {  // 记得加入onCalculate
  
  const [ quantity, setQuantity] = useState(0);
	  const increment = () => {
		setQuantity(quantity + 1);
        onCalculate(price);     //每次点击时执行 onCalculate(price) = totalCash + price
	};
  	const decrement = () => {
    if ( quantity > 0 ){
		  setQuantity(quantity - 1);  
          onCalculate(-price);   // 减少时为-price
    }
  };
  1. 制作Total元件,传入totalCash作为prop。
// total.js
import React, { useState } from "react";

const Total = ({totalCash}) => {
  
  return(
		<div>
			<h2>Total:{totalCash}</h2>
		</div>
	)	
};

export default Total

app.js要加入这一行

// app.js
      <Total totalCash={totalCash}/>

最後,执行看看清单的功能:
https://ithelp.ithome.com.tw/upload/images/20210922/20129476uVM6EiZEtu.png

可以成功进行增减了!!

最後附上Glitch完整程序码
(Glitch打开来可能要等有点久:(

【如内文有误还请不吝指教>< 并感谢阅览至此的各位:D 】

参考资料


<<:  Day 07 Create a Regression Model with Azure Machine Learning designer

>>:  【Side Project】 顾客点菜单画面设计

[资料库] 学习笔记 - 商城交易之上架商品

这次练习的题目是做出商城中上架商品的功能 功能主要需求:谁上架了什麽商品、上架数量多少,如果商品没有...

[Day 16] Reverse 小疲累

终於到星期五啦 明天就是周末六日了 今天也是我课最多的一天 从早八到五点连八堂 我遇到做图障碍的挫折...

Day 7 在 Linode 上购置一台 VPS 主机并安装 Docker

Linode 是提供多样化、不同规格的 VPS 服务器提供商。你可以藉由一个 Linode 帐号管理...

前端工程师也能开发全端网页:挑战 30 天用 React 加上 Firebase 打造社群网站|Day12 文章列表

连续 30 天不中断每天上传一支教学影片,教你如何用 React 加上 Firebase 打造社群...

【Day26】this - 物件的方法调用

在讲解 this 之前,先来看一段程序码,观察它的执行过程 var myName = 'weiwei...