
- 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 中所示。
