React 14 - 教程

state:组件的记忆案例练习

1 修复画廊翻页BUG代码详细解释

import { useState } from 'react';
import { sculptureList } from './data.js';
export default function Gallery() {
  const [index, setIndex] = useState(0);
  const [showMore, setShowMore] = useState(false);
  function handleNextClick() {
    setIndex(index + 1);
  }
  function handleMoreClick() {
    setShowMore(!showMore);
  }
  let sculpture = sculptureList[index];
  return (
    <>
      
      

{sculpture.name} by {sculpture.artist}

({index + 1} of {sculptureList.length})

{showMore &&

{sculpture.description}

} {sculpture.alt} ); }

改后

import { useState } from 'react';
import { sculptureList } from './data.js';
export default function Gallery() {
  const [index, setIndex] = useState(0);
  const [showMore, setShowMore] = useState(false);
  let hasPrev = index > 0;
  let hasNext = index < sculptureList.length - 1;
  function handlePrevClick() {
    if (hasPrev) {
      setIndex(index - 1);
    }
  }
  function handleNextClick() {
    if (hasNext) {
      setIndex(index + 1);
    }
  }
  function handleMoreClick() {
    setShowMore(!showMore);
  }
  let sculpture = sculptureList[index];
  return (
    <>
      
      
      

{sculpture.name} by {sculpture.artist}

({index + 1} of {sculptureList.length})

{showMore &&

{sculpture.description}

} {sculpture.alt} ); }

我们来详细分析这个代码的变化,理解它修复了什么问题以及为什么这样修复。

先看第一个版本的问题

第一个版本的代码中,只有一个Next按钮,它的点击事件处理函数是这样的:

function handleNextClick() {
  setIndex(index + 1);
}

这个函数的逻辑很简单:每次点击就把index(当前显示的雕塑索引)加 1。

这里隐藏的 bug 是:当浏览到最后一个雕塑时,继续点击Next按钮会导致错误。

  • 假设sculptureList有 5 个元素(索引 0-4),当index已经是 4(最后一个)时,点击Next会让index变成 5
  • 此时执行let sculpture = sculptureList[index]时,sculptureList[5]是不存在的(数组越界)
  • 后续访问sculpture.namesculpture.url等属性时,就会报错(无法访问undefined的属性)

修复方案:添加边界判断

第二个版本的代码主要做了 3 件事来修复这个问题:

  1. 增加了 “上一个” 按钮和对应的逻辑不仅能往后翻,还能往前翻,提升了用户体验。

  2. 添加了边界状态判断定义了两个变量来判断当前是否能翻页:

    let hasPrev = index > 0; // 是否有上一个(索引大于0时可以往前翻)
    let hasNext = index < sculptureList.length - 1; // 是否有下一个(索引小于最后一个索引时可以往后翻)
    • 比如数组长度为 5,最后一个元素的索引是 4,所以index < 4时才有下一个
  3. 在翻页函数中添加判断,并且禁用无效按钮

    • 翻页函数中先判断是否能翻页,只有能翻页时才修改index
      function handlePrevClick() {
        if (hasPrev) { // 只有有上一个时,才执行翻页
          setIndex(index - 1);
        }
      }
      function handleNextClick() {
        if (hasNext) { // 只有有下一个时,才执行翻页
          setIndex(index + 1);
        }
      }
    • 按钮添加disabled属性,当不能翻页时按钮变灰且无法点击:

总结修复的核心

  1. 防止数组越界:通过hasPrevhasNext判断边界,避免index超出数组的合法索引范围(0 到 数组长度 - 1)。
  2. 优化用户体验:禁用无效状态的按钮,让用户直观知道当前不能继续翻页,避免误操作。

这样修改后,无论怎么点击按钮,index都会保持在合法范围内,也就不会出现访问不存在的数组元素的错误了。

2 修复表单输入

export default function Form() {
  let firstName = '';
  let lastName = '';
  function handleFirstNameChange(e) {
    firstName = e.target.value;
  }
  function handleLastNameChange(e) {
    lastName = e.target.value;
  }
  function handleReset() {
    firstName = '';
    lastName = '';
  }
  return (
    
e.preventDefault()}>

Hi, {firstName} {lastName}

); }

改后

import { useState } from 'react';
export 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 (
    
e.preventDefault()}>

Hi, {firstName} {lastName}

); }

第一个版本的核心问题:普通变量无法触发组件重新渲染

第一个版本中,使用了普通的let变量来存储表单输入值:

let firstName = '';
let lastName = '';

当用户在输入框中输入内容时,handleFirstNameChangehandleLastNameChange确实会修改这两个变量的值。但React 并不知道这些变量的变化,所以不会重新渲染组件。

具体表现为:

  1. 输入框中输入内容时,输入框会 “卡住”,无法显示用户输入的内容(因为value={firstName}绑定的是不会被 React 追踪的普通变量)。
  2. 页面上的<h1>Hi, {firstName} {lastName}</h1>也不会随着输入更新,始终显示Hi, 
  3. 点击 “Reset” 按钮时,虽然变量被重置为'',但页面同样不会有任何变化。

这是因为React 组件的重新渲染只依赖于 “状态(state)” 或 “属性(props)” 的变化,普通变量的修改不会触发重新渲染。

第二个版本的修复:使用 React 状态(useState)

第二个版本引入了useState钩子来管理表单状态,这是修复问题的核心:

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

useState是 React 提供的用于管理组件内部状态的钩子,它的工作原理和作用如下:

  1. 状态变量(如 firstName):由useState创建的变量,React 会追踪它的变化。
  2. 状态更新函数(如 setFirstName):用于修改状态变量的值。关键是:调用这个函数后,React 会知道状态发生了变化,并自动重新渲染组件

修复后的具体变化分析

  1. 输入框与状态的双向绑定

    • 输入框的value绑定到状态变量(value={firstName}),确保输入框显示的是当前状态值。
    • 当用户输入时,onChange触发handleFirstNameChange,通过setFirstName(e.target.value)更新状态。
    • 状态更新后,React 重新渲染组件,输入框会显示新的状态值,实现了 “输入即显示” 的效果。
  2. 页面内容实时更新

    • 由于<h1>Hi, {firstName} {lastName}</h1>依赖状态变量,当状态更新并重新渲染时,这里的内容会自动更新为最新的输入值。
  3. Reset 按钮生效

    • 点击 “Reset” 时,handleReset通过setFirstName('')setLastName('')将状态重置为空。
    • 状态变化触发重新渲染,输入框和<h1>都会显示为空,实现了重置效果。

总结:核心修复逻辑

第一个版本的问题在于使用普通变量存储状态,变量变化无法被 React 感知,导致组件不重新渲染,页面无法更新。

第二个版本通过useState 钩子将普通变量转换为 React 状态,状态的每次更新(通过setXXX函数)都会触发组件重新渲染,从而让输入框、页面内容等依赖状态的部分实时更新,最终实现了表单的正常交互功能。

这也是 React 中 “状态驱动视图” 理念的体现:视图(UI)的变化应该由状态的变化来驱动,而状态必须通过 React 提供的机制(如useState)来管理,才能被 React 追踪和响应。

3 弹窗错误

import { useState } from 'react';
export default function FeedbackForm() {
  const [isSent, setIsSent] = useState(false);
  if (isSent) {
    return 

Thank you!

; } else { // eslint-disable-next-line const [message, setMessage] = useState(''); return (
{ e.preventDefault(); alert(`Sending: "${message}"`); setIsSent(true); }}>
posted @ 2025-12-02 12:30  gccbuaa  阅读(2)  评论(0)    收藏  举报