一、函数式组件的核心定义
React 函数式组件(Functional Component)是以 JavaScript 普通函数形式定义的 React 组件,是目前 React 开发的主流组件形式(替代传统类组件)。
核心特征
- 本质:一个接收
props参数并返回 React 元素(JSX 或 null)的纯函数(早期无 Hooks 时) - 无自身实例:相比类组件,函数式组件没有
this指向(无需处理this绑定问题) - 轻量简洁:代码结构更简单,无生命周期方法的冗余模板,逻辑内聚性更强
- 能力支撑: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) => newStateinitialState:初始状态对象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的执行时机,有三种情况:- 不传递依赖数组:组件每次渲染(挂载 + 更新)时都会执行副作用,清理函数在每次重新渲染前执行
- 传递空数组
[]:仅在组件挂载时执行一次(模拟componentDidMount),清理函数仅在组件卸载时执行一次(模拟componentWillUnmount) - 传递具体依赖(如
[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 / useReducer | this.state / setState |
| 生命周期/副作用 | useEffect 统一处理 | 明确的生命周期方法(如 componentDidMount) |
| 逻辑复用 | 自定义 Hooks(优雅无嵌套) | 高阶组件(HOC)/ Render Props(嵌套地狱) |
| 性能优化 | React.memo / useMemo / useCallback | shouldComponentUpdate / PureComponent |
| 代码简洁性 | 代码简洁,逻辑内聚 | 代码冗余,模板化严重 |
七、函数式组件的优势(为何成为主流)
- 无
this困扰:彻底避免了类组件中this指向混乱的问题,降低学习和开发成本 - 代码更简洁:去除了类组件的构造函数、生命周期等冗余模板,组件体积更小,可读性更强
- 逻辑复用更优雅:自定义 Hooks 可以将组件逻辑抽离为独立的可复用函数,无嵌套层级问题,比 HOC 更易维护
- 更好的性能优化:通过
React.memo、useMemo、useCallback可以更精细地控制组件渲染和计算逻辑 - 更好的兼容性:支持 React 最新特性(如 Server Components),是未来 React 开发的主流方向
总结
- 函数式组件是接收
props并返回 JSX 的普通函数,无this指向,轻量简洁 - 状态管理依赖
useState(简单状态)和useReducer(复杂状态),setState为异步更新 useEffect是副作用管理的核心,通过依赖数组模拟类组件的三个生命周期阶段- 性能优化的核心组合:
React.memo(组件缓存)+useMemo(计算缓存)+useCallback(函数缓存) - 函数式组件通过 Hooks 实现了类组件的所有能力,且在逻辑复用、代码简洁性上更具优势,是 React 开发的首选