ReactJS - 使用 React Hooks 进行状态管理



React 从 React 16.8 中引入了一个全新的概念,称为 React Hooks。尽管这是一个相对较新的概念,但它使 React 功能组件能够拥有自己的状态和生命周期。此外,React Hooks 使功能组件能够使用许多以前不可用的功能。在本章中,让我们看看如何使用 React Hooks 在功能组件中进行状态管理。

什么是 React Hooks?

React Hooks 是 React 提供的特殊函数,用于处理 React 功能组件内的特定功能。React 为每个受支持的特性提供了一个 Hook 函数。例如,React 提供了 useState() 函数来管理功能组件中的状态。当 React 功能组件使用 React Hooks 时,React Hooks 会将自身附加到组件中并提供额外的功能。

useState() Hook 的一般签名如下 -


 const [<state variable>, <state update function>] = useState(<initial value>);

例如,使用 Hooks 的时钟组件中的状态管理可以按照下面指定的方式完成 -


const [currentDateTime, setCurrentDateTime] = useState(new Date());	
setInterval(() => setCurrentDateTime(new Date()), 1000);

这里

  • currentDateTime − 用于保存当前日期和时间的变量(由 setState() 返回)
  • setCurrentDate() − 用于设置当前日期和时间的函数(由 setState() 返回))

创建有状态组件

在本章中,让我们使用 Hooks 重新创建时钟组件。

第 1 步 - 首先,按照创建 React 应用程序一章中的说明,使用 Create React App 或 Rollup bundler 创建一个新的 react 应用程序 react-clock-hook-app。

第 2 步 - 在您最喜欢的编辑器中打开应用程序。

在应用程序的根目录下创建 src 文件夹。

在 src 文件夹下创建 components 文件夹。

创建一个文件,Clock.js src/components 文件夹下并开始编辑。

导入 React 库和 React 状态 Hook、setState。


 import React, { useState } from 'react';

第 3 步 - 创建时钟组件。


function Clock() {	
}

创建状态 Hooks 以维护日期和时间。


 const [currentDateTime, setCurrentDateTime] = useState(new Date());

为每秒设置日期和时间。


 setInterval(() => setCurrentDateTime(new Date()), 1000);

创建用户界面以使用 currentDateTime 显示当前日期和时间,并返回它。


 return ( <div><p>The current time is {currentDateTime.toString()}</p></div> );

第 4 步 - 最后,使用代码片段导出组件 -


 export default Clock;

Clock组件的完整源代码如下:


import React, { useState } from 'react';

function Clock(props) {
	 	const [currentDateTime, setCurrentDateTime] = useState(new Date());
	 	setInterval(() => setCurrentDateTime(new Date()), 1000);
	 	return (
	 	 	 <div><p>The current time is {currentDateTime.toString()}</p></div>
	 	);
}
export default Clock;

index.js:

接下来,创建一个文件,index.js src 文件夹下并使用 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文件。


<!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

打开浏览器,在地址栏中输入 http://localhost:3000,然后按回车键。该应用程序将显示时间并每秒更新一次。

The current time is Wed Nov 11 2020 10:10:18 GMT+0530 (India Standard Time)

上述应用程序运行良好。但是,设置为每秒执行一次的 setCurrentDateTime() 在应用程序结束时必须删除。我们可以使用 React 提供的另一个 Hook,useEffect 来做到这一点。我们将在下一章(组件生命周期)中学习它。

在费用管理器应用程序中引入状态

在本章中,我们通过添加一个简单的功能来使用 Hooks 删除费用项目,从而在费用管理器应用程序中介绍状态管理。

第 1 步 - 在您最喜欢的编辑器中打开费用管理器应用程序。

创建一个新文件,ExpenseEntryItemListFn.js src/components 文件夹下并开始编辑。

导入 React 库和 React 状态 Hook、setState。


 import React, { useState } from 'react';

导入 css,ExpenseEntryItem.css。


 import './ExpenseEntryItemList.css'

第 2 步 - 创建 ExpenseEntryItemListFn 组件。


 function ExpenseEntryItemListFn(props) { }

使用通过属性传递到组件中的费用项初始化组件的状态钩子。


 const [items, setItems] = useState(props.items);

第 3 步 - 创建事件处理程序以突出显示行。


function handleMouseEnter(e) {
	 	e.target.parentNode.classList.add("highlight");
}
function handleMouseLeave(e) {
	 	e.target.parentNode.classList.remove("highlight");
}
function handleMouseOver(e) {
	 	console.log("The mouse is at (" + e.clientX + ", " + e.clientY + ")");
}

第 4 步 - 创建事件处理程序以使用 items 和 setItems() 删除所选项目。


function handleDelete(id, e) {
	 	e.preventDefault();
	 	console.log(id);
	 	let newItems = [];
	 	items.forEach((item, idx) => {
	 	 	 if (item.id != id)
	 	 	 	 	newItems.push(item)
	 	})
	 	setItems(newItems);
}

第 5 步 - 创建 getTotal() 方法以计算总金额。


function getTotal() {
	 	let total = 0;
	 	for (var i = 0; i < items.length; i++) {
	 	 	 total += items[i].amount
	 	}
	 	return total;
}

第 6 步 - 创建用户界面,通过循环覆盖项目来显示费用。


const lists = items.map((item) =>
	 	<tr key={item.id} onMouseEnter={handleMouseEnter} onMouseLeave={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) => handleDelete(item.id, e)}>Remove</a></td>
	 	</tr>
);

第 7 步 - 创建完整的 UI 以显示费用并将其返回。


return (
	 	<table onMouseOver={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" }}>
	 	 	 	 	 	 	 	{getTotal()}
	 	 	 	 	 	 </td>
	 	 	 	 	</tr>
	 	 	 </tbody>
	 	</table>
);

最后,导出函数,如下所示 -


 export default ExpenseEntryItemListFn;

ExpenseEntryItemListFn的完整代码如下:


import React, { useState } from 'react';
import './ExpenseEntryItemList.css'

function ExpenseEntryItemListFn(props) {
	 	const [items, setItems] = useState(props.items);

	 	function handleMouseEnter(e) {
	 	 	 e.target.parentNode.classList.add("highlight");
	 	}
	 	function handleMouseLeave(e) {
	 	 	 e.target.parentNode.classList.remove("highlight");
	 	}
	 	function handleMouseOver(e) {
	 	 	 console.log("The mouse is at (" + e.clientX + ", " + e.clientY + ")");
	 	}
	 	function handleDelete(id, e) {
	 	 	 e.preventDefault();
	 	 	 console.log(id);
	 	 	 let newItems = [];
	 	 	 items.forEach((item, idx) => {
	 	 	 	 	if (item.id != id)
	 	 	 	 	 	 newItems.push(item)
	 	 	 })
	 	 	 setItems(newItems);
	 	}
	 	function getTotal() {
	 	 	 let total = 0;
	 	 	 for (var i = 0; i < items.length; i++) {
	 	 	 	 	total += items[i].amount
	 	 	 }
	 	 	 return total;
	 	}
	 	const lists = items.map((item) =>
	 	 	 <tr key={item.id} onMouseEnter={handleMouseEnter} onMouseLeave={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) => handleDelete(item.id, e)}>Remove</a></td>
	 	 	 </tr>
	 	);
	 	return (
	 	 	 <table onMouseOver={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" }}>
	 	 	 	 	 	 	 	 	 {getTotal()}
	 	 	 	 	 	 	 	</td>
	 	 	 	 	 	 </tr>
	 	 	 	 	</tbody>
	 	 	 </table>
	 	);
}
export default ExpenseEntryItemListFn;

index.js

更新index.js并包含 ExpenseEntyItemListFn 组件 -


import React from 'react';
import ReactDOM from 'react-dom';
import ExpenseEntryItemListFn from './components/ExpenseEntryItemListFn'

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>
	 	 	 <ExpenseEntryItemListFn items={items} />
	 	</React.StrictMode>,
	 	document.getElementById('root')
);

接下来,使用 npm 命令为应用程序提供服务。

npm start

接下来,打开浏览器并在地址栏中输入 http://localhost:3000,然后按回车键。

最后,要删除支出项目,请单击相应的删除链接。它将删除相应的项目并刷新用户界面,如动画 gif 中所示。

接口