React Props和Hooks详解

引言
想象您正在用乐高积木搭建东西:
组件是您单独的乐高积木(<Button /><Profile /><Header />
Props是您给积木的指令
Hooks是您可以给积木的特殊能力
第一部分:Props(组件间如何通信)
核心概念
Props("properties"的缩写)用于将数据从组件传递给组件。这是一条单行道:数据只能向下流动。
类比:在餐厅点餐
您(父组件):您是点披萨的顾客
厨师(子组件):厨师是<Pizza />组件
您的订单(Props):您告诉厨师,"我想要一个大的披萨,加意大利辣香肠和额外奶酪。"这些具体指令——size: 'large'topping: 'pepperoni'extraCheese: true——就是props
厨师(<Pizza />)接收这些指令并按照您的要求制作披萨。厨师不能改变您的订单;他们只能读取并执行。这是props最重要的规则。
Props的关键特性
只读:子组件永远不能改变它接收到的props。它只能使用它们
数据向下流动:数据从父组件传递给子组件,再到孙组件,以此类推。子组件不能将props"向上"传递给父组件
它们是配置:Props是您配置和自定义可重用组件的方式
简单代码示例
让我们创建一个可重用的WelcomeMessage组件:
  1. // 这是我们的"厨师"组件。它是可重用的。
  2. // 它期望接收一个`name` prop。
  3. function WelcomeMessage({ name }) { // 我们"解构"props来直接获取name
  4. return (
  5. <h1>你好,{name}!欢迎来到我们的应用。h1>
  6. );
  7. }

  8. // 这是我们的"顾客"或父组件。
  9. function App() {
  10. return (
  11. <div>
  12. {/* 我们使用WelcomeMessage组件并给它props */}
  13. <WelcomeMessage name="张三" />
  14. <WelcomeMessage name="李四" />
  15. <WelcomeMessage name="王五" />
  16. div>
  17. );
  18. }
javascript
这里发生了什么?
App组件是父组件
它渲染WelcomeMessage组件三次
每次,它传递不同的name prop
WelcomeMessage组件接收那个name并显示它。我们创建了一个可重用的"模板",每次使用props进行不同配置
第二部分:Hooks(给组件记忆和超能力)
核心概念
在hooks之前,如果您想让组件有自己的内部"记忆"(状态)或在某事发生时做某事(生命周期事件),您必须使用繁琐的"类组件"。Hooks让您可以在简单、干净的"函数组件"中做所有这些事情。
Hooks是总是以单词use开头的特殊函数(例如,useStateuseEffect)。
让我们专注于两个最重要的hooks。
1. useState(记忆Hook)
类比:个人白板
想象您的组件旁边有一个小白板:
useState hook给您的组件这个白板
组件可以在板上写东西(初始状态
它可以看板上有什么(当前状态值
它得到一个特殊的魔法笔,这是唯一更新板上内容的方式(setter函数
当组件使用其魔法笔更新白板时,React知道某些东西已经改变,并自动重新渲染组件以显示新信息。
简单代码示例
让我们构建一个简单的计数器:
  1. import React, { useState } from 'react';

  2. function Counter() {
  3. // 1. 给我们的组件一个叫'count'的"白板"(状态)。
  4. // 初始值是0。
  5. // 我们得到当前值(`count`)和魔法笔(`setCount`)。
  6. const [count, setCount] = useState(0);

  7. return (
  8. <div>
  9. {/* 2. 显示白板上的当前值 */}
  10. <p>您点击了 {count}p>

  11. {/* 3. 当这个按钮被点击时,使用魔法笔更新白板 */}
  12. <button onClick={() => setCount(count + 1)}>
  13. 点击我
  14. button>
  15. div>
  16. );
  17. }
javascript
这里发生了什么?
useState(0)设置状态。count变量从0开始
组件渲染,显示"您点击了 0 "
当您点击按钮时,onClick函数调用setCount(count + 1)
React看到状态被更新了。它重新运行Counter函数
这次,count变量是1
组件再次渲染,显示"您点击了 1 "
2. useEffect(动作Hook)
类比:"在____发生之后,做这个。"
useEffect允许您的组件执行"副作用"——与React外部世界交互的动作。这包括:
在组件首次出现后从API获取数据
设置定时器
手动更改文档标题
它接受两个参数:
要运行的函数("effect"
依赖项数组("触发器"
依赖项数组告诉useEffect何时运行effect:
[](空数组):只在组件首次在屏幕上渲染后运行一次这个effect(非常适合初始数据获取)
[someVariable](带变量的数组):每次someVariable改变时运行这个effect
根本没有数组:在每次渲染后运行(这通常是一个错误,可能导致无限循环)
简单代码示例
让我们在Counter组件的计数改变时在控制台记录一条消息:
  1. import React, { useState, useEffect } from 'react';

  2. function CounterWithEffect() {
  3. const [count, setCount] = useState(0);

  4. // 这是我们的"动作Hook"
  5. useEffect(() => {
  6. // 这是effect:在控制台记录一条消息
  7. console.log(`新的计数是:${count}`);

  8. // 这是触发器:每当`count`改变时运行effect
  9. }, [count]);

  10. // 这个effect只在组件首次渲染后运行一次
  11. useEffect(() => {
  12. console.log('组件已经挂载!');
  13. }, []);

  14. return (
  15. <div>
  16. <p>您点击了 {count}p>
  17. <button onClick={() => setCount(count + 1)}>
  18. 点击我
  19. button>
  20. div>
  21. );
  22. }
javascript
这里发生了什么?
当组件首次渲染时,第二个useEffect运行,您会在控制台看到"组件已经挂载!"。第一个useEffect也运行,打印"新的计数是:0"
当您点击按钮时,调用setCount
状态改变,所以组件重新渲染
因为count改变了,第一个useEffect的触发器被激活,它再次运行,打印"新的计数是:1"。第二个useEffect不会再次运行,因为它的依赖项数组是空的
总结:Props vs Hooks
概念
Props
Hooks (useState, useEffect)
目的
将数据从父组件传递给子组件
给组件内部记忆和动作
数据流
自上而下(单向)
内部(管理自己的数据)
类比
配置/指令(在餐厅点餐)
记忆/能力(个人白板)
可以改变吗?
不能。子组件只读。
可以。通过其setter函数改变(例如,setCount)。
示例
<UserCard name="张三" />
const [count, setCount] = useState(0);
Props和Hooks一起工作。父组件可能使用useEffect获取数据,使用useState将其存储在状态中,然后将该数据的部分作为props传递给子组件。
实际应用示例
让我们看一个结合使用Props和Hooks的完整示例:
  1. import React, { useState, useEffect } from 'react';

  2. // 子组件:接收props
  3. function UserCard({ user, onUserClick }) {
  4. return (
  5. <div onClick={() => onUserClick(user.id)}>
  6. <h3>{user.name}h3>
  7. <p>{user.email}p>
  8. div>
  9. );
  10. }

  11. // 父组件:使用hooks管理状态
  12. function UserList() {
  13. const [users, setUsers] = useState([]);
  14. const [loading, setLoading] = useState(true);

  15. // 使用useEffect获取数据
  16. useEffect(() => {
  17. fetchUsers();
  18. }, []);

  19. const fetchUsers = async () => {
  20. try {
  21. const response = await fetch('https://api.example.com/users');
  22. const data = await response.json();
  23. setUsers(data);
  24. } catch (error) {
  25. console.error('获取用户失败:', error);
  26. } finally {
  27. setLoading(false);
  28. }
  29. };

  30. const handleUserClick = (userId) => {
  31. console.log('用户被点击:', userId);
  32. };

  33. if (loading) {
  34. return
    加载中...
    ;
  35. }

  36. return (
  37. {users.map(user => (
  38. key={user.id}
  39. user={user}
  40. onUserClick={handleUserClick}
  41. />
  42. ))}
  • );
  • }
  • javascript
    最佳实践
    Props最佳实践
    保持props简单:避免传递复杂的对象
    使用prop-types:验证props类型
    提供默认值:使用默认参数
    1. function Button({ text = '点击', onClick, disabled = false }) {
    2. return (
    3. <button onClick={onClick} disabled={disabled}>
    4. {text}
    5. button>
    6. );
    7. }
    javascript
    Hooks最佳实践
    只在顶层调用hooks:不要在循环、条件或嵌套函数中调用
    使用依赖项数组:避免无限循环
    清理副作用:在useEffect中返回清理函数
    1. useEffect(() => {
    2. const timer = setInterval(() => {
    3. console.log('定时器运行');
    4. }, 1000);

    5. // 清理函数
    6. return () => clearInterval(timer);
    7. }, []);
    javascript
    总结
    Props和Hooks是React开发的核心概念:
    Props让组件之间能够通信,传递数据和回调函数
    Hooks让函数组件拥有状态和副作用能力
    两者结合使用,可以构建强大而灵活的React应用
    掌握这些概念将帮助您构建更好的React组件和更复杂的应用程序。