- ReactJS 教程
- ReactJS - 首頁
- ReactJS - 簡介
- ReactJS - 路線圖
- ReactJS - 安裝
- ReactJS - 特性
- ReactJS - 優缺點
- ReactJS - 架構
- ReactJS - 建立 React 應用
- ReactJS - JSX
- ReactJS - 元件
- ReactJS - 巢狀元件
- ReactJS - 使用新建立的元件
- ReactJS - 元件集合
- ReactJS - 樣式
- ReactJS - 屬性 (props)
- ReactJS - 使用屬性建立元件
- ReactJS - props 驗證
- ReactJS - 建構函式
- ReactJS - 元件生命週期
- ReactJS - 事件管理
- ReactJS - 建立一個事件感知元件
- ReactJS - 在 Expense Manager 應用中引入事件
- ReactJS - 狀態管理
- ReactJS - 狀態管理 API
- ReactJS - 無狀態元件
- ReactJS - 使用 React Hooks 進行狀態管理
- ReactJS - 使用 React Hooks 的元件生命週期
- ReactJS - 佈局元件
- ReactJS - 分頁
- ReactJS - Material UI
- ReactJS - Http 客戶端程式設計
- ReactJS - 表單程式設計
- ReactJS - 受控元件
- ReactJS - 非受控元件
- ReactJS - Formik
- ReactJS - 條件渲染
- ReactJS - 列表
- ReactJS - Keys
- ReactJS - 路由
- ReactJS - Redux
- ReactJS - 動畫
- ReactJS - Bootstrap
- ReactJS - 地圖
- ReactJS - 表格
- ReactJS - 使用 Flux 管理狀態
- ReactJS - 測試
- ReactJS - CLI 命令
- ReactJS - 構建和部署
- ReactJS - 示例
- Hooks
- ReactJS - Hooks 簡介
- ReactJS - 使用 useState
- ReactJS - 使用 useEffect
- ReactJS - 使用 useContext
- ReactJS - 使用 useRef
- ReactJS - 使用 useReducer
- ReactJS - 使用 useCallback
- ReactJS - 使用 useMemo
- ReactJS - 自定義 Hooks
- ReactJS 高階
- ReactJS - 可訪問性
- ReactJS - 程式碼分割
- ReactJS - 上下文
- ReactJS - 錯誤邊界
- ReactJS - 轉發 Refs
- ReactJS - 碎片
- ReactJS - 高階元件
- ReactJS - 整合其他庫
- ReactJS - 最佳化效能
- ReactJS - Profiler API
- ReactJS - 埠
- ReactJS - 無 ES6 ECMAScript 的 React
- ReactJS - 無 JSX 的 React
- ReactJS - 調和
- ReactJS - Refs 和 DOM
- ReactJS - 渲染 Props
- ReactJS - 靜態型別檢查
- ReactJS - 嚴格模式
- ReactJS - Web Components
- 其他概念
- ReactJS - 日期選擇器
- ReactJS - Helmet
- ReactJS - 內聯樣式
- ReactJS - PropTypes
- ReactJS - BrowserRouter
- ReactJS - DOM
- ReactJS - 走馬燈
- ReactJS - 圖示
- ReactJS - 表單元件
- ReactJS - 參考 API
- ReactJS 有用資源
- ReactJS - 快速指南
- ReactJS - 有用資源
- ReactJS - 討論
ReactJS - 無狀態元件
具有內部狀態的 React 元件稱為有狀態元件,而沒有任何內部狀態管理的 React 元件稱為無狀態元件。React 建議儘可能建立和使用無狀態元件,只有在絕對必要時才建立有狀態元件。此外,React 不會與子元件共享狀態。資料需要透過子元件的屬性傳遞給子元件。
將日期傳遞給FormattedDate元件的示例如下:
<FormattedDate value={this.state.item.spend_date} />
總體思路是不使應用程式邏輯過於複雜,並且僅在必要時使用高階功能。
建立一個有狀態元件
讓我們建立一個 React 應用程式來顯示當前日期和時間。
步驟 1 - 首先,使用Create React App或Rollup捆綁器建立一個新的 React 應用程式react-clock-app,方法是按照建立 React 應用程式章節中的說明進行操作。
在您喜歡的編輯器中開啟應用程式。
步驟 2 - 在應用程式的根目錄下建立src資料夾。
在 src 資料夾下建立components資料夾。
在src/components資料夾下建立一個檔案Clock.js並開始編輯。
匯入React庫。
import React from 'react';
接下來,建立Clock元件。
class Clock extends React.Component {
constructor(props) {
super(props);
}
}
步驟 3 - 使用當前日期和時間初始化狀態。
constructor(props) {
super(props);
this.state = {
date: new Date()
}
}
步驟 4 - 新增一個方法setTime()來更新當前時間 -
setTime() {
console.log(this.state.date);
this.setState((state, props) => (
{
date: new Date()
}
))
}
步驟 5 - 使用 JavaScript 方法setInterval並每秒呼叫一次setTime()方法,以確保元件的狀態每秒更新一次。
constructor(props) {
super(props);
this.state = {
date: new Date()
}
setInterval( () => this.setTime(), 1000);
}
步驟 6 - 建立一個render函式。
render() {
}
Next, update the render() method to show the current time.
render() {
return (
<div><p>The current time is {this.state.date.toString()}</p></div>
);
}
最後,匯出元件。
export default Clock;
Clock 元件的完整原始碼如下:
import React from 'react';
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {
date: new Date()
}
setInterval( () => this.setTime(), 1000);
}
setTime() {
console.log(this.state.date);
this.setState((state, props) => (
{
date: new Date()
}
))
}
render() {
return (
<div>
<p>The current time is {this.state.date.toString()}</p>
</div>
);
}
}
export default Clock;
index.js
接下來,在 src 資料夾下建立一個檔案index.js並使用Clock元件。
import React from 'react';
import ReactDOM from 'react-dom';
import Clock from './components/Clock';
ReactDOM.render(
<React.StrictMode>
<Clock />
</React.StrictMode>,
document.getElementById('root')
);
index.html
最後,在根資料夾下建立一個public資料夾並建立index.html檔案。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Clock</title>
</head>
<body>
<div id="root"></div>
<script type="text/JavaScript" src="./index.js"></script>
</body>
</html>
使用 npm 命令啟動應用程式。
npm start
開啟瀏覽器並在位址列中輸入https://:3000並按 Enter 鍵。應用程式將顯示時間並每秒更新一次。
The current time is Wed Nov 11 2020 10:10:18 GMT+0530(Indian Standard Time)
上述應用程式工作正常,但在控制檯中丟擲錯誤。
Can't call setState on a component that is not yet mounted.
錯誤訊息表明,只有在元件掛載後才能呼叫 setState。
什麼是掛載?
React 元件有一個生命週期,而掛載是生命週期中的一個階段。讓我們在接下來的章節中詳細瞭解生命週期。
在費用管理應用中引入狀態
讓我們透過新增一個簡單的功能來刪除費用專案,在費用管理應用程式中引入狀態管理。
步驟 1 - 在您喜歡的編輯器中開啟expense-manager應用程式。
開啟ExpenseEntryItemList.js檔案。
使用透過屬性傳遞給元件的費用專案初始化元件的狀態。
this.state = {
items: this.props.items
}
步驟 2 - 在render()方法中新增刪除標籤。
<thead>
<tr>
<th>Item</th>
<th>Amount</th>
<th>Date</th>
<th>Category</th>
<th>Remove</th>
</tr>
</thead>
步驟 3 - 更新render()方法中的列表,以包含刪除連結。此外,使用狀態中的專案(this.state.items)而不是屬性中的專案(this.props.items)。
const lists = this.state.items.map((item) =>
<tr key={item.id} onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave}>
<td>{item.name}</td>
<td>{item.amount}</td>
<td>{new Date(item.spendDate).toDateString()}</td>
<td>{item.category}</td>
<td><a href="#" onClick={(e) => this.handleDelete(item.id, e)}>Remove</a></td>
</tr>
);
步驟 4 - 實現handleDelete方法,該方法將從狀態中刪除相關的費用專案。
handleDelete = (id, e) => {
e.preventDefault();
console.log(id);
this.setState((state, props) => {
let items = [];
state.items.forEach((item, idx) => {
if(item.id != id)
items.push(item)
})
let newState = {
items: items
}
return newState;
})
}
這裡,
費用專案是從元件的當前狀態中獲取的。
迴圈遍歷當前費用專案以查詢使用者使用專案 ID 引用的專案。
建立一個新的專案列表,其中包含除使用者引用的專案之外的所有費用專案
步驟 5 - 新增一個新行以顯示總費用金額。
<tr>
<td colSpan="1" style={{ textAlign: "right" }}>Total Amount</td>
<td colSpan="4" style={{ textAlign: "left" }}>
{this.getTotal()}
</td>
</tr>
步驟 6 - 實現getTotal()方法以計算總費用金額。
getTotal() {
let total = 0;
for(var i = 0; i < this.state.items.length; i++) {
total += this.state.items[i].amount
}
return total;
}
render()方法的完整程式碼如下:
render() {
const lists = this.state.items.map((item) =>
<tr key={item.id} onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave}>
<td>{item.name}</td>
<td>{item.amount}</td>
<td>{new Date(item.spendDate).toDateString()}</td>
<td>{item.category}</td>
<td><a href="#"
onClick={(e) => this.handleDelete(item.id, e)}>Remove</a></td>
</tr>
);
return (
<table onMouseOver={this.handleMouseOver}>
<thead>
<tr>
<th>Item</th>
<th>Amount</th>
<th>Date</th>
<th>Category</th>
<th>Remove</th>
</tr>
</thead>
<tbody>
{lists}
<tr>
<td colSpan="1" style={{ textAlign: "right" }}>Total Amount</td>
<td colSpan="4" style={{ textAlign: "left" }}>
{this.getTotal()}
</td>
</tr>
</tbody>
</table>
);
}
最後,ExpenseEntryItemList的更新程式碼如下:
import React from 'react';
import './ExpenseEntryItemList.css';
class ExpenseEntryItemList extends React.Component {
constructor(props) {
super(props);
this.state = {
items: this.props.items
}
this.handleMouseEnter = this.handleMouseEnter.bind();
this.handleMouseLeave = this.handleMouseLeave.bind();
this.handleMouseOver = this.handleMouseOver.bind();
}
handleMouseEnter(e) {
e.target.parentNode.classList.add("highlight");
}
handleMouseLeave(e) {
e.target.parentNode.classList.remove("highlight");
}
handleMouseOver(e) {
console.log("The mouse is at (" + e.clientX + ", " + e.clientY + ")");
}
handleDelete = (id, e) => {
e.preventDefault();
console.log(id);
this.setState((state, props) => {
let items = [];
state.items.forEach((item, idx) => {
if(item.id != id)
items.push(item)
})
let newState = {
items: items
}
return newState;
})
}
getTotal() {
let total = 0;
for(var i = 0; i < this.state.items.length; i++) {
total += this.state.items[i].amount
}
return total;
}
render() {
const lists = this.state.items.map((item) =>
<tr key={item.id} onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave}>
<td>{item.name}</td>
<td>{item.amount}</td>
<td>{new Date(item.spendDate).toDateString()}</td>
<td>{item.category}</td>
<td><a href="#"
onClick={(e) => this.handleDelete(item.id, e)}>Remove</a></td>
</tr>
);
return (
<table onMouseOver={this.handleMouseOver}>
<thead>
<tr>
<th>Item</th>
<th>Amount</th>
<th>Date</th>
<th>Category</th>
<th>Remove</th>
</tr>
</thead>
<tbody>
{lists}
<tr>
<td colSpan="1" style={{ textAlign: "right" }}>Total Amount</td>
<td colSpan="4" style={{ textAlign: "left" }}>
{this.getTotal()}
</td>
</tr>
</tbody>
</table>
);
}
}
export default ExpenseEntryItemList;
index.js
更新index.js幷包含ExpenseEntyItemList元件。
import React from 'react';
import ReactDOM from 'react-dom';
import ExpenseEntryItemList from './components/ExpenseEntryItemList'
const items = [
{ id: 1, name: "Pizza", amount: 80, spendDate: "2020-10-10", category: "Food" },
{ id: 2, name: "Grape Juice", amount: 30, spendDate: "2020-10-12", category: "Food" },
{ id: 3, name: "Cinema", amount: 210, spendDate: "2020-10-16", category: "Entertainment" },
{ id: 4, name: "Java Programming book", amount: 242, spendDate: "2020-10-15", category: "Academic" },
{ id: 5, name: "Mango Juice", amount: 35, spendDate: "2020-10-16", category: "Food" },
{ id: 6, name: "Dress", amount: 2000, spendDate: "2020-10-25", category: "Cloth" },
{ id: 7, name: "Tour", amount: 2555, spendDate: "2020-10-29", category: "Entertainment" },
{ id: 8, name: "Meals", amount: 300, spendDate: "2020-10-30", category: "Food" },
{ id: 9, name: "Mobile", amount: 3500, spendDate: "2020-11-02", category: "Gadgets" },
{ id: 10, name: "Exam Fees", amount: 1245, spendDate: "2020-11-04", category: "Academic" }
]
ReactDOM.render(
<React.StrictMode>
<ExpenseEntryItemList items={items} />
</React.StrictMode>,
document.getElementById('root')
);
使用 npm 命令啟動應用程式。
npm start
接下來,開啟瀏覽器並在位址列中輸入https://:3000並按 Enter 鍵。
最後,要刪除費用專案,請單擊相應的刪除連結。它將刪除相應的專案並重新整理使用者介面,如動畫 gif 中所示。