当前位置: 首页 > news >正文

React 18

往回啃啃啃,把开发里的实践和之前看的理论文档结合起来,react我学学学!

1 按钮button的禁用(disabled)属性控制

在前端开发中,按钮(button)的禁用状态是非常常用的交互设计,比如当操作不合法时(像你之前遇到的index已经是 0 时,“上一个” 按钮就应该禁用),通过禁用按钮可以直观地告诉用户 “这个操作现在不能执行”,同时防止无效点击。我们来详细了解一下:

1. 如何设置按钮禁用?

在 HTML 中,button元素有一个原生属性disabled,只要给它设置disabled(不管值是什么,甚至不需要值),按钮就会进入禁用状态:

<!-- 禁用状态的按钮 -->
<button disabled>我是禁用的按钮</button><!-- 非禁用状态(默认) -->
<button>我是可用的按钮</button>

在 React 中,因为要通过状态控制,所以会用 JSX 的语法,把disabled作为一个布尔值属性(注意 JSX 中属性名用驼峰式,不过disabled本身比较特殊,大小写不影响,但推荐小写):

// 用变量控制禁用状态
const isDisabled = true;
<button disabled={isDisabled}>禁用按钮</button>

2. 禁用状态的特点

  • 视觉上:浏览器会默认给禁用的按钮添加灰色样式(不同浏览器可能略有差异),文字也会变浅,用户一眼就能看出 “不可点击”。
  • 交互上:禁用的按钮不会响应点击事件(onClick不会触发),也无法被选中或聚焦(比如按 Tab 键跳不到这个按钮)。
  • 语义上:屏幕阅读器等辅助工具会识别disabled属性,告诉用户 “这个按钮当前不可用”,符合无障碍设计(a11y)要求。

3. 结合你的场景:动态控制禁用状态

回到你之前的index问题,当index为 0 时,“上一个” 按钮应该禁用。怎么实现呢?

步骤:

  1. 定义一个变量,判断是否需要禁用(比如index === 0时禁用)。
  2. 把这个变量传给按钮的disabled属性。

示例代码:

function MyComponent() {const [index, setIndex] = useState(0); // 假设初始index是0// 判断“上一个”按钮是否该禁用:当index为0时禁用const isPreviousDisabled = index === 0;function handlePreviousClick() {setIndex(index - 1);}return (<div>{/* 禁用状态由isPreviousDisabled控制 */}<button onClick={handlePreviousClick} disabled={isPreviousDisabled}>上一个</button></div>);
}

这样,当index为 0 时,按钮会变成禁用状态,点击也不会触发handlePreviousClick(即使你不写之前的if判断,也不会有问题,相当于双重保险)。

4. 自定义禁用样式

浏览器默认的禁用样式可能不符合你的 UI 设计,这时可以通过 CSS 自定义:

/* 禁用状态的按钮样式 */
button:disabled {background-color: #f0f0f0; /* 浅灰色背景 */color: #999; /* 灰色文字 */cursor: not-allowed; /* 鼠标悬停时显示“不允许”图标 */border: 1px solid #ddd;/* 可以去掉默认的禁用透明度 */opacity: 1;
}

注意 CSS 中用button:disabled选择器来定位禁用状态的按钮。

5. 为什么需要禁用按钮?

  • 提升用户体验:明确告诉用户当前操作不可行,避免用户困惑(比如点了没反应,用户可能以为按钮坏了)。
  • 减少无效逻辑:虽然你可以在onClick里用if判断是否执行操作,但禁用按钮从源头就阻止了点击,更高效。
  • 符合交互规范:是前端交互的通用设计模式,用户看到禁用按钮会自然理解 “现在不能点”。

试着在你之前的代码里加上disabled属性控制,看看效果吧~ 比如当index为 0 时,“上一个” 按钮变灰且点不动,是不是更合理了?

2 target属性

target,这是事件对象(比如前面说的e)里一个非常核心的属性,我们可以深入理解一下它的含义和用法。

1. target 到底指什么?

e.target 表示的是触发当前事件的那个最底层的 DOM 元素。简单说,就是 “谁直接引发了这个事件”。

举个例子:

<div onClick={(e) => console.log(e.target)}><button>点击我</button>
</div>

当你点击按钮时,事件会从按钮(最底层元素)向上传递到外层的div。这时候,e.target 指向的是按钮(因为是按钮直接被点击),而不是外层的div

2. 对比一个容易混淆的属性:currentTarget

为了更好理解target,可以对比一下e.currentTargetcurrentTarget 指的是绑定了当前事件处理函数的那个元素

还是上面的例子:

<div onClick={(e) => {console.log('target:', e.target); // 输出 <button>console.log('currentTarget:', e.currentTarget); // 输出 <div>
}}><button>点击我</button>
</div>
  • 事件处理函数绑在div上,所以currentTargetdiv
  • 但事件是由button触发的,所以targetbutton

这就是它们的核心区别:target是事件的 “源头”,currentTarget是事件处理函数的 “宿主”

3. 实际开发中的用法

target最常用的场景,就是在事件冒泡时精准定位 “到底点了哪个元素”。

比如一个列表,每个列表项里有文字和删除按钮,点击不同部分做不同处理:

<ul onClick={(e) => {// 如果点击的是删除按钮(假设按钮有 class="delete-btn")if (e.target.classList.contains('delete-btn')) {console.log('删除当前项');} // 如果点击的是列表项的文字部分else if (e.target.tagName === 'SPAN') {console.log('查看详情');}
}}><li><span>第一项内容</span><button class="delete-btn">删除</button></li><li><span>第二项内容</span><button class="delete-btn">删除</button></li>
</ul>

这里利用e.target判断点击的具体元素,避免了给每个按钮和文字单独绑定事件,更高效(这种方式叫 “事件委托”)。

4. 总结

e.target 的核心意义是:找到触发事件的具体元素,它让我们能在复杂的 DOM 结构中精准处理用户交互。

你可以试着写一个包含多层嵌套元素的结构,给外层绑定点击事件,然后打印e.targete.currentTarget,观察它们的区别,这样理解会更深刻~

3 写处理表单错误百出的代码改错

不自己动手写写真以为掌握了,眼睛会了但是手还没会,脑子也没回,来暴露一下基础不牢地动山摇的代码。

export default function Form() {const [firstName ,setFirstName ] =('');const[ lastName ,setLastName] = ('');function handleFirstNameChange(e) {setFirstName(e.target.value);}function handleLastNameChange(e) {setLastName = (e.target.value);}function handleReset() {setFirstName('');setLastName('');}return (<form onSubmit={e => e.preventDefault()}><inputplaceholder="First name"value={firstName}onChange={handleFirstNameChange}/><inputplaceholder="Last name"value={lastName}onChange={handleLastNameChange}/><h1>Hi, {firstName} {lastName}</h1><button onClick={handleReset}>Reset</button></form>);
}
//为啥没有默认显示lastname,还有我写的逻辑对吗

你的代码里有几个小问题,导致可能出现 “lastName 没有默认显示” 或者逻辑不生效的情况,我们一步步来分析和修正,顺便梳理表单处理的逻辑~

一、首先看明显的语法错误

  1. useState的使用错误你写的是:

    const [firstName, setFirstName] = ('');
    const [lastName, setLastName] = ('');
    

    这是错的!useState是 React 的钩子函数,必须通过useState()调用,正确写法是:

    const [firstName, setFirstName] = useState(''); // 初始化值为''
    const [lastName, setLastName] = useState('');
    

    并且需要在文件顶部导入useState

    import { useState } from 'react';
    

    (如果不导入,React 会报错 “useState is not defined”)

  2. setLastName的赋值错误handleLastNameChange里,你写的是:

    setLastName = (e.target.value); // 错误!
    

    setLastNameuseState返回的状态更新函数,应该通过调用它来更新状态,而不是给它赋值。正确写法是:

    setLastName(e.target.value); // 调用函数,传入新值
    

二、为什么 “lastName 没有默认显示”?

默认情况下,firstNamelastName的初始值都是''(空字符串),所以页面加载时h1里会显示 “Hi,”(两个空字符串拼接),这是正常的。

如果输入内容后lastName依然不显示,很大概率是上面第 2 个错误导致的:因为setLastName = ...是赋值操作,并没有真正更新lastName状态,所以输入时lastName始终是初始的'',自然不显示。

三、整体逻辑是否正确?

修正上述错误后,核心逻辑是对的:

  • useState管理两个输入框的状态(firstNamelastName),属于 “受控组件” 模式;
  • onChange事件触发时,通过e.target.value获取输入值,再用setXxx更新状态;
  • handleReset函数通过setXxx('')重置状态,输入框会同步清空,符合预期;
  • form标签添加onSubmit={e => e.preventDefault()}阻止默认提交行为,避免页面刷新,这也是正确的。

四、修正后的完整代码

import { useState } from 'react'; // 导入useStateexport default function Form() {// 正确初始化状态const [firstName, setFirstName] = useState('');const [lastName, setLastName] = useState('');function handleFirstNameChange(e) {setFirstName(e.target.value); // 正确更新状态}function handleLastNameChange(e) {setLastName(e.target.value); // 这里修正为调用函数}function handleReset() {setFirstName('');setLastName('');}return (<form onSubmit={e => e.preventDefault()}><inputplaceholder="First name"value={firstName}onChange={handleFirstNameChange}/><inputplaceholder="Last name"value={lastName}onChange={handleLastNameChange}/><h1>Hi, {firstName} {lastName}</h1><button onClick={handleReset}>Reset</button></form>);
}

五、拓展:优化代码的小技巧

你会发现handleFirstNameChangehandleLastNameChange逻辑几乎一样,只是更新的状态不同。可以合并成一个函数,通过参数区分要更新的状态,减少重复代码:

// 合并后的处理函数:接收一个参数(要更新的状态名)
function handleInputChange(e, field) {if (field === 'firstName') {setFirstName(e.target.value);} else if (field === 'lastName') {setLastName(e.target.value);}
}// 调用时传入对应的字段名
<inputplaceholder="First name"value={firstName}onChange={(e) => handleInputChange(e, 'firstName')} // 注意这里用箭头函数传参
/>
<inputplaceholder="Last name"value={lastName}onChange={(e) => handleInputChange(e, 'lastName')}
/>

这样当表单字段增多时(比如添加邮箱、电话),不需要写多个重复的处理函数,代码更简洁~

试着按照修正后的代码运行一下,看看输入内容后firstNamelastName是否能正常显示,重置按钮是否能清空内容。如果还有问题,可以告诉我具体现象,我们再进一步分析~

【事件处理函数中参数的匹配问题?为什么handleInputChange(e, field) 里的 e 和 field 为什么不会混淆,为什么 e 总能拿到事件对象,而 field 是我们手动传的参数。】

这其实涉及到 React 事件处理的参数传递机制,我们一步步理清楚:

1. 先明确:事件处理函数的第一个参数默认是事件对象

当你给元素绑定事件(比如 onChangeonClick)时,React 会在事件触发时自动传递一个事件对象作为第一个参数。这个规则和原生 JavaScript 一致。

比如最简单的情况:

<input onChange={(e) => console.log(e)} />

这里的 e 不需要我们手动传,React 会自动把事件对象塞进去,所以你总能通过第一个参数拿到事件对象(比如 e.target.value)。

2. 当需要额外参数时,如何避免混淆?

像你之前的 handleInputChange(e, field),需要同时用到事件对象 e 和我们自定义的 field 参数。这时候要注意:React 只会自动传事件对象作为第一个参数,额外的参数需要我们手动传递

具体做法是用箭头函数包裹,显式传递参数:

// 正确写法:用箭头函数接收 React 自动传的 e,再手动加上 field 参数
onChange={(e) => handleInputChange(e, 'firstName')}

这里的执行过程是:

  1. 输入框变化时,React 触发 onChange,自动传入事件对象 e
  2. 箭头函数 (e) => ... 接收这个 e
  3. 箭头函数调用 handleInputChange 时,手动把 e 和 'firstName' 作为参数传进去。
  4. 所以 handleInputChange 的第一个参数 e 就是事件对象,第二个参数 field 就是 'firstName',不会混淆。

3. 为什么不会误判成 value

value 是事件对象 e.target 的一个属性(e.target.value),而我们传的 field 是一个独立的参数(比如字符串 'firstName'),两者在 “身份” 和 “用途” 上完全不同:

  • e 是事件对象,包含了事件的所有信息(触发元素、值、类型等),是 React 自动传递的。
  • field 是我们自己定义的标记(比如用来区分 “firstName” 还是 “lastName”),是手动传递的额外参数。

函数内部通过参数位置(第一个是 e,第二个是 field)来区分,所以不会把 field 误当成 e.target.value

4. 反例:如果不注意参数顺序会怎样?

如果写成这样(参数顺序反了):

// 错误示例:参数顺序反了
function handleInputChange(field, e) { // 这里的 field 会变成事件对象 e,e 会变成 'firstName',完全混乱console.log(field.target.value); // 报错!因为 'firstName' 没有 target 属性
}// 调用时
onChange={(e) => handleInputChange('firstName', e)} 

这时候就会混淆,因为参数顺序和函数定义的顺序不匹配。所以关键是保证传递的参数顺序和函数接收的顺序一致

总结

  • React 事件处理函数的第一个参数默认是事件对象 e(自动传递)。
  • 额外的参数需要通过箭头函数手动传递,且要保证参数顺序和函数定义一致
  • e.target.value 是事件对象里的属性,而自定义参数(如 field)是独立的,通过参数位置区分,不会混淆。

你可以试着故意写错参数顺序,看看控制台会报什么错,这样能更直观地理解参数匹配的重要性~

模板字符串(Template Literals) 语法

你问的${name}涉及到 JavaScript 中的模板字符串(Template Literals) 语法,这是 ES6 引入的一种字符串处理方式,非常实用。我们来详细理解它:

1. 什么是模板字符串?

模板字符串用 ** 反引号()** 包裹(不是单引号或双引号),里面可以通过 ${变量或表达式}` 的形式嵌入变量或计算结果,比传统的字符串拼接更简洁。

比如你的代码里:

alert(`Hello, ${name}!`);

等价于用传统字符串拼接的写法:

alert("Hello, " + name + "!"); // 效果完全一样

但模板字符串的写法更直观,尤其是拼接多个变量时优势明显。

2. 模板字符串的核心特点

  • 用反引号(`)包裹:这是区分模板字符串和普通字符串的标志。
  • ${} 用于嵌入内容${} 内部可以放变量、函数调用、算术运算等,会自动计算并转换为字符串。

举几个例子:

const age = 20;
const message = `I am ${age + 5} years old`; // 嵌入表达式,结果是 "I am 25 years old"function getGreeting() {return "Hello";
}
const text = `${getGreeting()}, world!`; // 嵌入函数调用,结果是 "Hello, world!"

3. 你的代码里需要注意的点

虽然模板字符串的用法是对的,但你的代码有个小问题:alert 里的 name 可能不是最新值

原因是:setName 是异步的(React 会批量更新状态以提高性能),所以在 handleClick 中,调用 setName 后立即访问 name,拿到的还是更新前的旧值。

比如初始 name 是 '',当你在 prompt 中输入 Alice 后:

  • setName('Alice') 会请求更新状态,但不会立即生效。
  • 此时 alert(Hello, ${name}!) 中的 name 还是旧值 '',所以会弹出 Hello, !

解决办法:用 prompt 的返回值直接拼接(因为此时我们已经知道新值了):

function handleClick() {const newName = prompt('What is your name?'); // 先保存新值setName(newName); // 更新状态alert(`Hello, ${newName}!`); // 用新值拼接,确保正确
}

总结

  • ${} 是模板字符串中嵌入变量 / 表达式的语法,用反引号包裹字符串。
  • 优势:比 + 拼接更简洁,尤其适合多变量拼接场景。
  • 注意:React 状态更新是异步的,更新后立即访问状态可能拿到旧值,需用临时变量保存新值。

你可以试着修改代码,用临时变量 newName 来显示 alert,看看是否能正确弹出输入的名字~

http://www.dtcms.com/a/601416.html

相关文章:

  • CVPR 2025|电子科大提出渐进聚焦Transformer:显著降低超分辨率计算开销
  • CTFHub Web进阶-Linux:动态装载
  • Nginx域名与SSL证书配置完整流程
  • 美食网站要怎么做自己做相册的网站
  • 全国 网站备案 数量电子设计工程官网
  • 一、UDP以太网帧格式
  • 网络协议设计原则简介和资料推荐
  • 有哪些程序网站品牌和商标的区别
  • C语言编译器选择指南 | 初学者实用教程
  • 小智机器人MCP
  • 对于给不良网站发律师函如何做收银系统哪个软件好
  • 网站管理工作总结安阳县事业单位招聘2021
  • RK-Android11-修改系统的鼠标光标
  • vs2022 IDE扩展无法卸载/VSI 插件卸载及实例清理
  • 华为OD机试 双机位A卷 - 智能驾驶 (JAVA Python C++ JS GO)
  • 广州网站制作实力乐云seo怎么提高网站的流量
  • 开源工具 yt-dlp 超简易上手版
  • 郑州网站优化外包顾问上海网站建设 分类广告
  • SSM基于WEB的教学质量评价系统的设计与实现p9ak6(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
  • PCB常用规则
  • Node.js 集成百度语音
  • 求一个网站开发接项目的平台做网站的外包需要分享客户信息
  • 怎么用PS做网站横幅wordpress 抽奖
  • 【开题答辩全过程】以 基于Vue.js和Node.js的在线旅游平网站的设计和实现为例,包含答辩的问题和答案
  • MySQL快速入门——用户管理
  • 中咨城建设计南京网站网站建设南昌
  • 大连网站制作流程在哪里可以免费自学seo课程
  • [智能体设计模式]第3章 并行化
  • Prisma 原生 SQL 多表联查完整指南
  • 哪个网站原创文章旅游集团网站建设