今天要解释的是: 如何将下层State提升给上层元件。
但如果没有举例真的太抽象了,所以就乾脆做个功能,边进行解释吧。
功能包含:
实作步骤:
我们先制作一个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
目前会是一个有【商品名称】【价格】【数量】【增减按钮】的清单
但是现在只有渲染画面,按按钮清单也不为所动,所以要开始帮他加上一些功能:
增加数量
预设的商品数量是0,所以这时候可以帮【数量】设useState(0)
:
const [ quantity, setQuantity] = useState(0); // quantity 预设值= 0
...
<h3>quantity:{quantity}</h3> //渲染至画面
可以看到现在的【数量】是 0:
接着先帮"+"按钮做增加数量的功能,宣告一个increment
的函式:
const increment = () => {
setQuantity(quantity + 1); // 可以想成 quantity = quantity + 1
};
然後要记得帮button绑上功能:
<button onClick={increment}>+</button> // 点击时执行increment(); quantity + 1
可以按按看"+",会增加数量(被我按到5了):
减少数量
宣告decrement
函式,在数量>0时,执行quantity - 1
:
const decrement = () => {
if ( quantity > 0 ){
setQuantity(quantity - 1); // quantity = quantity - 1
}
};
因为商品不能是负数,所以这里简单加了一个if的判断式。
然後帮button绑上decrement
的功能:
<button onClick={decrement}>-</button>
到这里应该可以成功减少数量了:
接着,我们要让更多商品应用同样的功能,所以要使用刚刚完成的Product元件。
架构可以想像成这样:
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
到这里可以试试看购物清单的功能:
应该可以成功的显示商品和增减数量了!
最後也是最重要的,计算总价。
假设我们要制作另一个元件Total显示总价,直觉上的架构可能会长这样:
但这个结构会产生一个问题: TotalCach
要怎麽知道每个元件的state的变化?
通常来说,有一些 component 需要反映相同的资料变化。我们建议将共享的 state 提升到最靠近它们的共同 ancestor。
所以可能要改变一下结构, 可以直接把totalCash提升为元件App的State,
同时把totalCash作为props传入元件Total。
// app.js
const [ totalCash, setTotalCash ] = useState(0); // totalCash预设值为0
const calculate = (price) => {
setTotalCash( totalCash + price); // totalCash = totalCash + price
}
总价的预设值为0,总价随着价格增减,後面会解释得比较仔细。
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}
。
// 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
}
};
// 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}/>
最後,执行看看清单的功能:
可以成功进行增减了!!
最後附上Glitch完整程序码
(Glitch打开来可能要等有点久:(
【如内文有误还请不吝指教>< 并感谢阅览至此的各位:D 】
参考资料
<<: Day 07 Create a Regression Model with Azure Machine Learning designer
这次练习的题目是做出商城中上架商品的功能 功能主要需求:谁上架了什麽商品、上架数量多少,如果商品没有...
终於到星期五啦 明天就是周末六日了 今天也是我课最多的一天 从早八到五点连八堂 我遇到做图障碍的挫折...
Linode 是提供多样化、不同规格的 VPS 服务器提供商。你可以藉由一个 Linode 帐号管理...
连续 30 天不中断每天上传一支教学影片,教你如何用 React 加上 Firebase 打造社群...
在讲解 this 之前,先来看一段程序码,观察它的执行过程 var myName = 'weiwei...