创见博客
React的函数组件
七崽爱吃小饼干2026/01/05阅读 0专栏 React

一、函数式组件的核心定义

React 函数式组件(Functional Component)是以 JavaScript 普通函数形式定义的 React 组件,是目前 React 开发的主流组件形式(替代传统类组件)。

核心特征

  1. 本质:一个接收 props 参数并返回 React 元素(JSX 或 null)的纯函数(早期无 Hooks 时)
  2. 无自身实例:相比类组件,函数式组件没有 this 指向(无需处理 this 绑定问题)
  3. 轻量简洁:代码结构更简单,无生命周期方法的冗余模板,逻辑内聚性更强
  4. 能力支撑:React 16.8 引入 Hooks 后,函数式组件拥有了状态管理、生命周期模拟等完整能力,完全可替代类组件

基础结构(最简示例)

jsx
// 1. 无状态基础函数式组件(接收 props,返回 JSX)
function Greeting(props) {
  // 从 props 中解构属性,更简洁
  const { name, message } = props;
  return (
    <div>
      <h1>Hello, {name}!</h1>
      <p>{message}</p>
    </div>
  );
}

// 2. 箭头函数形式(更常用,写法简洁)
const Greeting = (props) => {
  const { name, message } = props;
  return (
    <div>
      <h1>Hello, {name}!</h1>
      <p>{message}</p>
    </div>
  );
};

// 3. 使用组件
function App() {
  return <Greeting name="React 函数式组件" message="这是基础函数式组件示例" />;
}

export default App;

二、函数式组件的状态管理(Hooks 核心)

早期函数式组件被称为「无状态组件」,Hooks 推出后,通过 useState 和 useReducer 实现了组件内部状态管理,这是函数式组件的核心能力之一。

1. 基础状态管理:useState

useState 是最常用的状态 Hook,用于声明单个状态变量,支持简单状态的管理。

核心语法

javascript
const [state, setState] = useState(initialValue);
  • initialValue:状态初始值(可以是任意类型:数字、字符串、对象、数组等,也可以是函数(惰性初始化,用于复杂计算场景)
  • state:当前状态值,用于在组件中读取状态
  • setState:状态更新函数,用于修改状态值,调用后会触发组件重新渲染

完整示例

jsx
import React, { useState } from 'react';

// 计数器组件:使用 useState 管理状态
const Counter = () => {
  // 1. 声明状态:count 初始值为 0
  // 2. 惰性初始化(适用于初始值需要复杂计算时):const [count, setCount] = useState(() => 0);
  const [count, setCount] = useState(0);

  // 状态更新:两种方式
  const increment = () => {
    // 方式1:直接传递新值(不依赖当前状态)
    setCount(count + 1);
  };

  const decrement = () => {
    // 方式2:函数式更新(依赖当前状态,推荐,避免异步更新误差)
    setCount(prevCount => prevCount - 1);
  };

  return (
    <div>
      <p>当前计数:{count}</p>
      <button onClick={increment}>+1</button>
      <button onClick={decrement}>-1</button>
    </div>
  );
};

export default Counter;

关键特性

  • setState 是异步更新的(与类组件 setState 一致),React 会批量合并更新以提升性能
  • 对于对象/数组类型的状态,setState 是「替换式更新」,而非「合并式更新」,需要手动合并原有状态
    jsx
    // 对象类型状态示例
    const [user, setUser] = useState({ name: '张三', age: 18 });
    
    const updateAge = () => {
      // 手动合并原有状态,只更新 age 属性
      setUser(prevUser => ({ ...prevUser, age: prevUser.age + 1 }));
    };
    

2. 复杂状态管理:useReducer

当组件状态逻辑复杂(多个状态关联更新、状态更新依赖复杂判断)时,useReducer 比 useState 更适用,其思想借鉴了 Redux。

核心语法

javascript
const [state, dispatch] = useReducer(reducer, initialState, init?);
  • reducer:纯函数,接收 (prevState, action) 两个参数,返回新状态,格式:(prevState, action) => newState
  • initialState:初始状态对象
  • init(可选):惰性初始化函数,用于延迟计算初始状态
  • state:当前状态值
  • dispatch:分发动作的函数,用于触发 reducer 执行,从而更新状态,参数 action 通常是包含 type 属性的对象

完整示例

jsx
import React, { useReducer } from 'react';

// 1. 定义 reducer 函数:处理复杂状态逻辑
const counterReducer = (prevState, action) => {
  switch (action.type) {
    case 'INCREMENT':
      return { ...prevState, count: prevState.count + 1 };
    case 'DECREMENT':
      return { ...prevState, count: prevState.count - 1 };
    case 'RESET':
      return { ...prevState, count: 0 };
    case 'SET_VALUE':
      return { ...prevState, count: action.payload };
    default:
      // 未知 action 类型,返回原有状态
      return prevState;
  }
};

// 2. 计数器组件:使用 useReducer 管理状态
const ComplexCounter = () => {
  // 声明状态和 dispatch 函数
  const [state, dispatch] = useReducer(counterReducer, { count: 0 });

  return (
    <div>
      <p>当前计数:{state.count}</p>
      {/* 分发不同 action,触发状态更新 */}
      <button onClick={() => dispatch({ type: 'INCREMENT' })}>+1</button>
      <button onClick={() => dispatch({ type: 'DECREMENT' })}>-1</button>
      <button onClick={() => dispatch({ type: 'RESET' })}>重置</button>
      <button onClick={() => dispatch({ type: 'SET_VALUE', payload: 10 })}>设为10</button>
    </div>
  );
};

export default ComplexCounter;

三、生命周期与副作用管理:useEffect

函数式组件中没有类组件那样明确的生命周期方法,而是通过 useEffect Hook 统一处理副作用(如网络请求、定时器、DOM 操作、事件监听等),并模拟类组件的生命周期阶段。

1. useEffect 核心语法

javascript
useEffect(() => {
  // 副作用执行逻辑(相当于 componentDidMount + componentDidUpdate)
  const effectFn = () => {
    // 具体副作用操作
  };
  effectFn();

  // 清理函数(可选,相当于 componentWillUnmount + 下一次副作用执行前)
  return () => {
    // 清理副作用:清除定时器、取消网络请求、解绑事件等
  };
}, [dependencies]); // 依赖数组(可选)

2. 核心参数说明

  • 第一个参数:副作用回调函数,执行具体的副作用逻辑,可选返回一个清理函数
  • 第二个参数:依赖数组(可选),用于控制 useEffect 的执行时机,有三种情况:
    1. 不传递依赖数组:组件每次渲染(挂载 + 更新)时都会执行副作用,清理函数在每次重新渲染前执行
    2. 传递空数组 []:仅在组件挂载时执行一次(模拟 componentDidMount),清理函数仅在组件卸载时执行一次(模拟 componentWillUnmount)
    3. 传递具体依赖(如 [count, name]):仅当依赖数组中的某个值发生变化时,才会执行副作用,清理函数在依赖变化后、新副作用执行前执行

3. 模拟类组件生命周期示例

jsx
import React, { useState, useEffect } from 'react';

const LifecycleDemo = () => {
  const [count, setCount] = useState(0);

  // 1. 模拟 componentDidMount(仅挂载时执行一次)
  useEffect(() => {
    console.log('组件已挂载到 DOM');
    // 发起网络请求、初始化第三方库、添加定时器等
    const timer = setInterval(() => {
      console.log('定时器执行中...');
    }, 1000);

    // 模拟 componentWillUnmount(组件卸载时执行清理)
    return () => {
      console.log('组件即将卸载,清理副作用');
      clearInterval(timer); // 清除定时器,防止内存泄漏
    };
  }, []); // 空依赖数组

  // 2. 模拟 componentDidMount + componentDidUpdate(依赖 count 变化)
  useEffect(() => {
    console.log(`count 值发生变化:当前 count = ${count}`);
    // 当 count 变化时,执行相关逻辑(如重新发起请求)
  }, [count]); // 依赖 count,仅 count 变化时执行

  // 3. 不传递依赖数组:每次渲染都执行
  // useEffect(() => {
  //   console.log('组件渲染(挂载/更新)');
  // });

  return (
    <div>
      <p>count: {count}</p>
      <button onClick={() => setCount(count + 1)}>+1</button>
    </div>
  );
};

export default LifecycleDemo;

四、函数式组件的 Props 处理

函数式组件接收 props 作为参数,处理方式简洁灵活,支持默认值、类型校验等特性。

1. 基础接收与解构

jsx
// 方式1:参数直接解构
const UserCard = ({ name, age, gender = '未知' }) => {
  return (
    <div>
      <h3>{name}</h3>
      <p>年龄:{age}</p>
      <p>性别:{gender}</p>
    </div>
  );
};

// 方式2:内部解构
const UserCard = (props) => {
  const { name, age, gender = '未知' } = props;
  return (
    <div>
      <h3>{name}</h3>
      <p>年龄:{age}</p>
      <p>性别:{gender}</p>
    </div>
  );
};

// 使用组件
<UserCard name="李四" age={20} />

2. 默认 Props 两种写法

jsx
// 写法1:解构赋值时设置默认值(推荐,更简洁)
const UserCard = ({ name = '匿名用户', age = 0, gender = '未知' }) => {
  return <div>...</div>;
};

// 写法2:通过组件属性设置默认值
const UserCard = (props) => {
  const { name, age, gender } = props;
  return <div>...</div>;
};
// 设置默认 props
UserCard.defaultProps = {
  name: '匿名用户',
  age: 0,
  gender: '未知'
};

3. Props 类型校验(使用 PropTypes)

jsx
import React from 'react';
import PropTypes from 'prop-types';

const UserCard = ({ name, age, gender }) => {
  return (
    <div>
      <h3>{name}</h3>
      <p>年龄:{age}</p>
      <p>性别:{gender}</p>
    </div>
  );
};

// 定义 Props 类型校验
UserCard.propTypes = {
  name: PropTypes.string.isRequired, // 字符串类型,必填
  age: PropTypes.number, // 数字类型,可选
  gender: PropTypes.oneOf(['男', '女', '未知']) // 枚举类型
};

export default UserCard;

五、函数式组件的性能优化

函数式组件的性能优化主要围绕「避免不必要的重新渲染」和「减少重复计算」展开,核心使用 React.memo、useMemo、useCallback 三个 API。

1. React.memo:组件级缓存(避免不必要渲染)

React.memo 是一个高阶组件,用于缓存函数式组件的渲染结果,当组件的 props 没有发生变化时,跳过组件的重新渲染(类似类组件的 PureComponent)。

用法示例

jsx
import React, { useState, memo } from 'react';

// 用 memo 包裹组件,实现 props 浅比较缓存
const ChildComponent = memo(({ name, age }) => {
  console.log('子组件重新渲染了');
  return (
    <div>
      <p>姓名:{name}</p>
      <p>年龄:{age}</p>
    </div>
  );
});

// 父组件
const ParentComponent = () => {
  const [count, setCount] = useState(0);
  const [user, setUser] = useState({ name: '张三', age: 18 });

  return (
    <div>
      <p>父组件 count:{count}</p>
      <button onClick={() => setCount(count + 1)}>父组件+1</button>
      {/* 子组件 props 未变化时,不会随父组件重新渲染 */}
      <ChildComponent name={user.name} age={user.age} />
    </div>
  );
};

export default ParentComponent;
  • 特性:React.memo 默认进行「浅比较」,若 props 是复杂对象(深层属性变化),需要手动传递第二个参数(比较函数)进行深层比较。

2. useMemo:缓存计算结果(减少重复计算)

useMemo 用于缓存复杂计算的结果,仅当依赖项发生变化时,才会重新执行计算,避免每次渲染都进行昂贵的计算操作。

用法示例

jsx
import React, { useState, useMemo } from 'react';

const ExpensiveCalculation = ({ list }) => {
  // 复杂计算:过滤并排序数组(模拟昂贵计算)
  const sortedList = useMemo(() => {
    console.log('执行复杂计算...');
    return list.filter(item => item > 0).sort((a, b) => a - b);
  }, [list]); // 仅 list 变化时,重新计算

  return (
    <div>
      <p>排序后的数组:{sortedList.join(', ')}</p>
    </div>
  );
};

const Parent = () => {
  const [count, setCount] = useState(0);
  const [list, setList] = useState([3, -1, 5, 2, -4]);

  return (
    <div>
      <p>count:{count}</p>
      <button onClick={() => setCount(count + 1)}>+1</button>
      <ExpensiveCalculation list={list} />
    </div>
  );
};

export default Parent;

3. useCallback:缓存函数引用(避免子组件不必要渲染)

useCallback 用于缓存函数的引用,当依赖项未变化时,返回同一个函数引用,配合 React.memo 使用,可避免因函数引用变化导致子组件不必要的重新渲染。

用法示例

jsx
import React, { useState, memo, useCallback } from 'react';

// 子组件:用 memo 缓存
const Child = memo(({ onHandleClick }) => {
  console.log('子组件重新渲染');
  return <button onClick={onHandleClick}>子组件按钮</button>;
});

// 父组件
const Parent = () => {
  const [count, setCount] = useState(0);

  // 用 useCallback 缓存函数引用,仅依赖变化时更新
  const handleClick = useCallback(() => {
    console.log('按钮被点击');
  }, []); // 空依赖,函数引用始终不变

  return (
    <div>
      <p>count:{count}</p>
      <button onClick={() => setCount(count + 1)}>父组件+1</button>
      <Child onHandleClick={handleClick} />
    </div>
  );
};

export default Parent;

六、函数式组件 vs 类组件(核心差异回顾)

特性函数式组件(Hooks)类组件
定义形式普通函数/箭头函数ES6 Class(继承 Component)
this 指向无 this,无需处理绑定存在 this 绑定问题
状态管理useState / useReducerthis.state / setState
生命周期/副作用useEffect 统一处理明确的生命周期方法(如 componentDidMount)
逻辑复用自定义 Hooks(优雅无嵌套)高阶组件(HOC)/ Render Props(嵌套地狱)
性能优化React.memo / useMemo / useCallbackshouldComponentUpdate / PureComponent
代码简洁性代码简洁,逻辑内聚代码冗余,模板化严重

七、函数式组件的优势(为何成为主流)

  1. 无 this 困扰:彻底避免了类组件中 this 指向混乱的问题,降低学习和开发成本
  2. 代码更简洁:去除了类组件的构造函数、生命周期等冗余模板,组件体积更小,可读性更强
  3. 逻辑复用更优雅:自定义 Hooks 可以将组件逻辑抽离为独立的可复用函数,无嵌套层级问题,比 HOC 更易维护
  4. 更好的性能优化:通过 React.memo、useMemo、useCallback 可以更精细地控制组件渲染和计算逻辑
  5. 更好的兼容性:支持 React 最新特性(如 Server Components),是未来 React 开发的主流方向

总结

  1. 函数式组件是接收 props 并返回 JSX 的普通函数,无 this 指向,轻量简洁
  2. 状态管理依赖 useState(简单状态)和 useReducer(复杂状态),setState 为异步更新
  3. useEffect 是副作用管理的核心,通过依赖数组模拟类组件的三个生命周期阶段
  4. 性能优化的核心组合:React.memo(组件缓存)+ useMemo(计算缓存)+ useCallback(函数缓存)
  5. 函数式组件通过 Hooks 实现了类组件的所有能力,且在逻辑复用、代码简洁性上更具优势,是 React 开发的首选
评论
0/100