Skip to content

第17章:React 常见问题与避坑指南

在学习和使用 React 的过程中,新手往往会遇到各种问题和错误。本章将总结一些常见的问题和解决方案,帮助你快速识别和解决这些问题,避免在开发过程中走弯路。

17.1 新手高频错误(语法错误、状态修改错误、路由配置错误)

17.1.1 语法错误

1. JSX 语法错误

错误示例

jsx
// 错误:JSX 中使用了未闭合的标签
function App() {
  return (
    <div>
      <h1>Hello World
    </div>
  );
}

// 错误:JSX 中使用了 class 而不是 className
function App() {
  return (
    <div class="container">
      <h1>Hello World</h1>
    </div>
  );
}

// 错误:JSX 中使用了内联样式的错误写法
function App() {
  return (
    <div style="color: red; font-size: 16px;">
      <h1>Hello World</h1>
    </div>
  );
}

解决方案

  • 确保所有标签都正确闭合
  • 在 JSX 中使用 className 替代 class
  • 内联样式应该使用对象形式:style={ { color: 'red', fontSize: '16px' } }

2. 变量和函数命名错误

错误示例

jsx
// 错误:使用了保留关键字作为变量名
function App() {
  const const = "Hello";
  return <div>{const}</div>;
}

// 错误:函数名使用了小写开头(虽然不会报错,但不符合规范)
function app() {
  return <div>Hello World</div>;
}

解决方案

  • 避免使用 JavaScript 保留关键字作为变量名
  • 组件名应该使用 PascalCase(首字母大写)命名规范

17.1.2 状态修改错误

1. 直接修改状态

错误示例

jsx
function App() {
  const [count, setCount] = useState(0);
  
  const handleClick = () => {
    // 错误:直接修改状态
    count = count + 1;
  };
  
  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={handleClick}>Increment</button>
    </div>
  );
}

解决方案

  • 使用 setState 函数来修改状态:setCount( count + 1 )
  • 对于复杂状态(对象、数组),应该创建新的副本:
    jsx
    // 正确的对象状态修改
    setUser(prevUser => ({ ...prevUser, name: 'New Name' }));
    
    // 正确的数组状态修改
    setItems(prevItems => [...prevItems, newItem]);

2. 状态更新的异步性

错误示例

jsx
function App() {
  const [count, setCount] = useState(0);
  
  const handleClick = () => {
    setCount(count + 1);
    // 错误:这里的 count 仍然是旧值
    console.log(count); // 输出:0
  };
  
  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={handleClick}>Increment</button>
    </div>
  );
}

解决方案

  • 使用函数式更新来获取最新状态:
    jsx
    setCount(prevCount => {
      console.log(prevCount); // 输出:当前最新值
      return prevCount + 1;
    });
  • 或者使用 useEffect 来监听状态变化:
    jsx
    useEffect(() => {
      console.log(count); // 输出:最新值
    }, [count]);

17.1.3 路由配置错误

1. React Router v6 语法错误

错误示例

jsx
// 错误:使用了 React Router v5 的语法
import { Switch, Route } from 'react-router-dom';

function App() {
  return (
    <Switch>
      <Route path="/" component={Home} />
      <Route path="/about" component={About} />
    </Switch>
  );
}

解决方案

  • 使用 React Router v6 的新语法:
    jsx
    import { Routes, Route } from 'react-router-dom';
    
    function App() {
      return (
        <Routes>
          <Route path="/" element={<Home />} />
          <Route path="/about" element={<About />} />
        </Routes>
      );
    }

2. 路由参数错误

错误示例

jsx
// 错误:使用了错误的方式获取路由参数
import { useParams } from 'react-router-dom';

function User() {
  // 错误:直接解构可能不存在的参数
  const { id } = useParams();
  
  return <div>User ID: {id}</div>;
}

解决方案

  • 始终检查参数是否存在:
    jsx
    import { useParams } from 'react-router-dom';
    
    function User() {
      const params = useParams();
      const id = params.id;
      
      if (!id) {
        return <div>User ID not found</div>;
      }
      
      return <div>User ID: {id}</div>;
    }

17.2 常见报错解析(如Cannot read property 'xxx' of undefined)

17.2.1 Cannot read property 'xxx' of undefined

错误示例

jsx
function App() {
  const [user, setUser] = useState(null);
  
  return (
    <div>
      {/* 错误:当 user 为 null 时,访问 user.name 会报错 */}
      <p>User: {user.name}</p>
    </div>
  );
}

解决方案

  • 使用条件渲染:
    jsx
    return (
      <div>
        {user ? <p>User: {user.name}</p> : <p>Loading...</p>}
      </div>
    );
  • 使用可选链操作符(ES2020+):
    jsx
    return (
      <div>
        <p>User: {user?.name || 'Loading...'}</p>
      </div>
    );

17.2.2 Cannot read property 'map' of undefined

错误示例

jsx
function App() {
  const [items, setItems] = useState(); // 初始值为 undefined
  
  return (
    <div>
      {/* 错误:当 items 为 undefined 时,调用 map 方法会报错 */}
      {items.map(item => (
        <div key={item.id}>{item.name}</div>
      ))}
    </div>
  );
}

解决方案

  • 为状态设置默认值:
    jsx
    const [items, setItems] = useState([]); // 初始值为空数组
  • 使用条件渲染:
    jsx
    return (
      <div>
        {items && items.map(item => (
          <div key={item.id}>{item.name}</div>
        ))}
      </div>
    );

17.2.3 Expected server HTML to contain a matching < div > in < div >

错误示例

jsx
// 服务端渲染时的错误
function App() {
  const [count, setCount] = useState(0);
  
  return (
    <div>
      {count % 2 === 0 ? <div>Even</div> : <span>Odd</span>}
    </div>
  );
}

解决方案

  • 确保服务端和客户端渲染的 HTML 结构一致:
    jsx
    function App() {
      const [count, setCount] = useState(0);
      
      return (
        <div>
          <div>{count % 2 === 0 ? 'Even' : 'Odd'}</div>
        </div>
      );
    }

17.2.4 Maximum update depth exceeded

错误示例

jsx
function App() {
  const [count, setCount] = useState(0);
  
  // 错误:在渲染过程中直接调用 setCount,导致无限循环
  setCount(count + 1);
  
  return (
    <div>
      <p>Count: {count}</p>
    </div>
  );
}

解决方案

  • 不要在渲染过程中直接修改状态
  • 将状态修改放在事件处理函数或 useEffect 中:
    jsx
    function App() {
      const [count, setCount] = useState(0);
      
      useEffect(() => {
        // 只在组件挂载时执行一次
        setCount(1);
      }, []);
      
      return (
        <div>
          <p>Count: {count}</p>
          <button onClick={() => setCount(count + 1)}>Increment</button>
        </div>
      );
    }

17.3 开发规范(组件命名、文件目录、代码格式)

17.3.1 组件命名规范

  • 组件名:使用 PascalCase(首字母大写),例如 UserProfileTodoList
  • 文件名:与组件名保持一致,使用 PascalCase,例如 UserProfile.jsxTodoList.jsx
  • 函数名:使用 camelCase(首字母小写),例如 handleClickfetchData
  • 变量名:使用 camelCase,例如 userNameisLoading
  • 常量名:使用 UPPER_SNAKE_CASE,例如 API_URLMAX_COUNT

17.3.2 文件目录结构

src/
├── components/          # 可复用组件
│   ├── Button/          # 组件文件夹
│   │   ├── Button.jsx   # 组件文件
│   │   ├── Button.css   # 组件样式
│   │   └── index.js     # 导出文件
│   └── Card/
├── pages/               # 页面组件
│   ├── Home.jsx
│   ├── About.jsx
│   └── Contact.jsx
├── hooks/               # 自定义 Hooks
│   ├── useCounter.js
│   └── useLocalStorage.js
├── utils/               # 工具函数
│   ├── format.js
│   └── api.js
├── assets/              # 静态资源
│   ├── images/
│   └── styles/
├── App.jsx              # 应用根组件
└── main.jsx             # 应用入口

17.3.3 代码格式规范

  • 缩进:使用 2 个空格或 4 个空格进行缩进(保持一致)
  • 换行:在 JSX 中,每个元素应该单独占一行
  • 括号:使用大括号包围 JSX 中的 JavaScript 表达式
  • 分号:根据团队规范使用或不使用分号
  • 引号:JSX 属性使用双引号,JavaScript 字符串使用单引号

示例

jsx
// 良好的代码格式
function UserProfile({ user, onUpdate }) {
  const [isEditing, setIsEditing] = useState(false);
  
  const handleEdit = () => {
    setIsEditing(true);
  };
  
  return (
    <div className="user-profile">
      <h2>{user.name}</h2>
      <p>Email: {user.email}</p>
      {!isEditing ? (
        <button onClick={handleEdit}>Edit</button>
      ) : (
        <EditForm user={user} onUpdate={onUpdate} onCancel={() => setIsEditing(false)} />
      )}
    </div>
  );
}

17.4 调试技巧(VS Code调试、React DevTools使用)

17.4.1 VS Code 调试

1. 配置 launch.json

在项目根目录创建 .vscode/launch.json 文件:

json
{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Chrome",
      "type": "chrome",
      "request": "launch",
      "url": "http://localhost:3000",
      "webRoot": "${workspaceFolder}/src",
      "sourceMaps": true
    }
  ]
}

2. 设置断点

在代码中点击行号旁边的空白区域设置断点,然后点击 VS Code 左侧的调试图标,选择 "Chrome" 配置并点击运行按钮。

3. 使用 console.log

在代码中添加 console.log() 语句来输出变量值和执行流程:

jsx
function App() {
  const [count, setCount] = useState(0);
  
  const handleClick = () => {
    console.log('Before increment:', count);
    setCount(count + 1);
    console.log('After increment:', count); // 注意:这里仍然是旧值
  };
  
  useEffect(() => {
    console.log('Count updated:', count);
  }, [count]);
  
  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={handleClick}>Increment</button>
    </div>
  );
}

17.4.2 React DevTools 使用

1. 安装 React DevTools

  • Chrome 浏览器:在 Chrome Web Store 中搜索 "React DevTools" 并安装
  • Firefox 浏览器:在 Firefox Add-ons 中搜索 "React DevTools" 并安装

2. 组件检查

  • 打开浏览器开发者工具,切换到 "Components" 选项卡
  • 查看组件树结构,点击组件查看其 props 和 state
  • 修改组件的 props 和 state 来测试组件行为

3. Profiler 性能分析

  • 切换到 "Profiler" 选项卡
  • 点击 "Record" 按钮开始记录
  • 执行一些操作(如点击按钮、输入文本等)
  • 点击 "Stop" 按钮停止记录
  • 查看组件渲染时间和次数,找出性能瓶颈

4. 常见使用场景

  • 查看组件状态:快速了解组件的当前状态和 props
  • 调试状态变化:观察状态如何随着用户操作而变化
  • 性能优化:识别渲染次数过多的组件
  • 组件树分析:了解组件的嵌套关系和结构

小结

本章总结了 React 开发中常见的问题和解决方案,包括:

  • 新手高频错误:语法错误、状态修改错误、路由配置错误
  • 常见报错解析:Cannot read property 'xxx' of undefined、Cannot read property 'map' of undefined 等
  • 开发规范:组件命名、文件目录、代码格式
  • 调试技巧:VS Code 调试、React DevTools 使用

通过了解这些常见问题和解决方案,你可以在开发过程中避免许多陷阱,提高开发效率。记住,遇到问题时,仔细阅读错误信息,使用调试工具,并且不要害怕查阅官方文档和社区资源。

© 2026 编程马·菜鸟教程 版权所有