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