react 概念
常用HOOK

开发理念
父组件(或页面、布局)默认是服务端组件(Server Component),它负责数据获取、SEO、渲染静态内容,性能更好。
子组件只在需要交互、状态管理、浏览器 API 等客户端功能时,单独加 "use client",让它成为客户端组件(Client Component)。
这样父组件在服务器渲染,子组件在客户端渲染,二者共存,互不冲突。
核心概念
模块 | 内容 |
---|---|
JSX | JSX 是语法糖,必须包裹单一元素,支持表达式,不支持语句;可以使用 fragment (<> </> ) 避免多余标签 |
组件 | 函数组件 vs 类组件(目前推荐函数组件 + Hook) |
Props | 父组件向子组件传值,具备只读特性(不可修改 props) |
State | 状态驱动视图,useState 管理局部状态 |
事件绑定 | 推荐箭头函数避免 this 问题;事件对象不等同于原生 event(是合成事件) |
渲染流程
模块 | 内容 |
---|---|
渲染流程 | 初次渲染(mount)、更新渲染(update)、卸载渲染(unmount) |
生命周期 | 函数组件用 useEffect 模拟 componentDidMount/WillUnmount |
批量更新 | React 会合并 setState 操作,且异步处理(除事件处理外) |
依赖收集 | useEffect/useMemo/useCallback 中依赖数组漏写会导致 BUG 或死循环 |
Hooks 使用规范
要点 | 说明 |
---|---|
顺序一致 | Hook 不能在条件语句或循环中调用,必须固定顺序 |
自定义 Hook | 可复用状态逻辑,命名规范:useXxx |
useEffect 清理函数 | 返回的函数会在组件卸载或下次执行前调用,用于解绑操作等 |
useCallback 与 useMemo 的区别 | 一个是缓存函数,一个是缓存结果(避免不必要渲染) |
组件设计与通信
模块 | 内容 |
---|---|
父传子 | props |
子传父 | 回调函数传入子组件,子组件中触发 |
跨层通信 | Context |
状态提升 | 多个子组件共享状态,提升至最近公共祖先 |
控制组件 vs 非控制组件 | 表单字段值由 React 控制(受控),还是由 DOM 控制(非受控) |
性能优化(中大型项目必须掌握)
优化点 | 工具 |
---|---|
避免重复渲染 | React.memo 、useCallback 、useMemo |
避免虚渲染 | 条件渲染提前返回(如 if (!data) return null ) |
拆分组件 | 避免一个组件 render 过多逻辑 |
异步渲染控制 | useTransition (React 18)、懒加载 React.lazy |
事件节流防抖 | 自定义 useThrottle / useDebounce |
服务端渲染(SSR)与同构应用(Next.js)
要点 | 内容 |
---|---|
SSR 原理 | 首屏由服务端渲染 HTML,客户端接管(hydration) |
数据获取 | getServerSideProps / getStaticProps (Next.js) |
动态路由 | useRouter / [slug].tsx |
Head 管理 | next/head / Metadata API |
SEO 优化 | 添加 meta、og、title、img 等信息 |
图片优化 | next/image 自动懒加载、压缩 |
动画与交互(提升体验)
模块 | 工具 |
---|---|
页面切换动画 | Framer Motion、Transition Group |
滚动监听 | useScroll 、IntersectionObserver |
星星移动背景 | 自定义 canvas or DOM 随鼠标偏移实现 |
Loading 状态 | Skeleton、Spinner、Suspense fallback |
常见陷阱和误区(避免踩坑)
陷阱 | 说明 |
---|---|
setState 不立即更新 | 是异步的,依赖上一次值时要用函数式 setState(prev => ...) |
useEffect 忘记依赖 | 会造成 stale 问题或死循环 |
多次请求未清理 | 请求未取消就卸载页面,导致内存泄漏(需加 AbortController ) |
子组件重新渲染频繁 | 函数 props 每次重建(用 useCallback 优化) |
依赖外部变量未 memo | 如 useEffect 中使用的外部对象或函数未作为依赖传入 |
调试技巧(调试工具 + 开发习惯)
工具 | 作用 |
---|---|
React DevTools | 查看组件树、props、hooks、性能分析等 |
Profiler | 性能分析,找出渲染瓶颈 |
console.trace() | 查看状态变化调用路径 |
状态打印 | 利用 useEffect(() => console.log(state), [state]) |
推荐常用库
类别 | 工具 |
---|---|
表单管理 | react-hook-form 、formik |
状态管理 | zustand 、redux 、jotai |
数据请求 | swr 、react-query |
动画 | framer-motion |
响应式布局 | tailwindcss |
组件库 | shadcn/ui 、chakra-ui 、antd |
组件用法
函数式写法
1<!-- 定义约束父组件传过来的事件 参数类型 --> 2interface Props { 3 photos: Photo[]; 4 currentIdx: number; 5 onClose: () => void; 6 onChangeIndex: (i: number) => void; 7} 8<!-- 使用方式 --> 9export default function ClientLightbox({ 10 photos, 11 currentIdx, 12 onClose, 13 onChangeIndex, 14}: Props) { 15 return ( 16 <Lightbox 17 open 18 slides={photos.map((p) => ({ 19 src: p.url, 20 title: p.tip, 21 description: p.desc, 22 }))} 23 index={currentIdx} 24 close={onClose} 25 onSlideChange={onChangeIndex} 26 plugins={[Captions]} 27 render={{ 28 slide: ({ slide }: { slide: { src: string } }) => ( 29 <ZoomableCanvas url={slide.src} /> 30 ), 31 }} 32 /> 33 ); 34} 35
类式写法
1 2<!-- 该写法不常用 --> 3import React from 'react'; 4 5class Greeting extends React.Component { 6 render() { 7 return <div>Hello, {this.props.name}</div>; 8 } 9} 10
样式定义
外部引入
1<!-- 外部定义一个.css文件 --> 2 3<!-- 内部引入 --> 4 5import styles from '../page.module.css'; 6 7<!-- 使用 --> 8 <section className={styles['page-bg']}></section> 9<!-- 或 --> 10 <section className={styles.sectionTitle}></section>
内联样式
1 <section 2 ref={ref} 3 className={styles.card} 4 onClick={onClick} 5 style={{ width: '100%', height: 'auto' }} 6 ></section> 7
Taiwindcss
1<!-- 全局或单个导入 --> 2@import 'tailwindcss'; 3@tailwind utilities; 4/* Tailwind 基础导入 */ 5 6 <section className="min-h-screen bg-gradient-to-br from-purple-600 pt-20 overflow-x-hidden"> 7 </section>
常用 HOOK
useState-- 状态管理
原理:
React 在组件首次渲染时,初始化状态值。
setState 更新状态后,组件重新渲染,useState 返回最新状态。
状态通过 React 内部 Fiber 链表结构按调用顺序管理,保证每次渲染状态对应正确。
特点:
支持基本类型、对象、数组等任意类型状态。
每次更新只会影响当前组件
多次调用可创建多个独立状态
异步批量更新(可使用回调函数安全读取旧值)
用法:
1import React, { useState } from 'react'; 2 3export default function Counter() { 4 // 初始化状态count为0,setCount是更新count的函数 5 const [count, setCount] = useState(0); 6 7 return ( 8 <div> 9 <p>当前计数:{count}</p> 10 <button onClick={() => setCount(count + 1)}>点击增加</button> 11 </div> 12 ); 13} 14
常见面试题:
1setState 是同步还是异步? 2👉 React 内部会批量处理 setState,合并触发一次渲染,因此是“批量异步执行”,但在原生事件中是同步更新。
useEffect --副作用处理
原理
用于执行副作用逻辑(如请求数据、订阅事件、定时器),依赖数组控制副作用执行时机。
特点
没有依赖数组时,每次渲染后都会执行。
有依赖数组时,只有依赖变化时才执行。
可以返回清理函数,类似 class 组件的 componentWillUnmount。
用法
1import { useEffect, useState } from 'react'; 2 3export default function Timer() { 4 const [time, setTime] = useState(0); 5 6 useEffect(() => { 7 const id = setInterval(() => setTime(t => t + 1), 1000); 8 return () => clearInterval(id); // 清理定时器 9 }, []); 10 11 return <div>秒数:{time}</div>; 12} 13
常见面试题:
1依赖数组为何不能随意漏? 2👉 忽略依赖会导致闭包内使用旧值或内存泄漏。
useContext--跨组件共享数据
原理
借助 React.createContext 创建上下文,子组件可直接访问祖先提供的数据。
特点
避免繁琐的 props 传递
常用于全局主题、用户信息等
所依赖的值变化会触发子组件更新
用法
1import { createContext, useContext } from 'react'; 2 3const ThemeContext = createContext('light'); 4 5function Child() { 6 const theme = useContext(ThemeContext); 7 return <div>当前主题:{theme}</div>; 8} 9 10export default function App() { 11 return ( 12 <ThemeContext.Provider value="dark"> 13 <Child /> 14 </ThemeContext.Provider> 15 ); 16} 17
常见面试题:
useRef--持久引用 / DOM 操作
原理
返回一个带有 .current 属性的对象,该对象在组件重渲染时保持不变。
特点
修改 .current 不会触发组件更新
常用于:保存定时器 ID、访问 DOM 节点、跨渲染缓存值
用法
1import { useRef, useEffect } from 'react'; 2 3export default function FocusInput() { 4 const inputRef = useRef<HTMLInputElement>(null); 5 6 useEffect(() => { 7 inputRef.current?.focus(); // 自动聚焦 8 }, []); 9 10 return <input ref={inputRef} />; 11}
常见面试题:
useReducer-- 管理复杂状态
原理
通过 dispatch(action) 调用 reducer 函数,返回新的 state。
特点
状态逻辑复杂时更适合 than useState
和 Redux 概念类似,但是局部管理
用法
1import { useReducer } from 'react'; 2 3function reducer(state, action) { 4 switch (action.type) { 5 case 'increment': return { count: state.count + 1 }; 6 case 'decrement': return { count: state.count - 1 }; 7 default: return state; 8 } 9} 10 11export default function Counter() { 12 const [state, dispatch] = useReducer(reducer, { count: 0 }); 13 14 return ( 15 <> 16 <p>计数:{state.count}</p> 17 <button onClick={() => dispatch({ type: 'increment' })}>+</button> 18 19 ); 20} 21
常见面试题:
useMemo --缓存计算结果
原理
只在依赖变化时重新计算,避免重复复杂计算。
特点
用于性能优化。
避免重复计算、子组件不必要的渲染
用法
1import { useMemo, useState } from 'react'; 2 3export default function ExpensiveCalc() { 4 const [count, setCount] = useState(0); 5 6 const result = useMemo(() => { 7 console.log('重新计算'); 8 return count * 100; 9 }, [count]); 10 11 return ( 12 <> 13 <p>结果:{result}</p> 14 <button onClick={() => setCount(c => c + 1)}>+</button> 15 </> 16 ); 17}
常见面试题:
1useMemo 和普通函数调用的区别? 2👉 useMemo 会缓存,避免每次都重新执行。
useCallback -- 缓存函数引用
原理
只有依赖项变化时才重新生成函数引用。
特点
优化传递给子组件的函数 props,避免组件重复渲染
通常与 memo 搭配使用
用法
1import { useCallback, useState } from 'react'; 2 3export default function CallbackDemo() { 4 const [count, setCount] = useState(0); 5 6 const handleClick = useCallback(() => { 7 console.log('当前 count:', count); 8 }, [count]); 9 10 return <button onClick={handleClick}>点击</button>; 11}
常见面试题:
其他扩展
Hook | 说明 |
---|---|
useLayoutEffect | 与 useEffect 类似,但同步执行于 DOM 更新后(适合测量 DOM 尺寸) |
useImperativeHandle | 与 forwardRef 一起使用,自定义对外暴露的 ref 方法 |
useId | 生成唯一 ID,适用于服务端渲染场景 |
useTransition | React 18 用于异步 UI 更新,避免阻塞主渲染 |
useDeferredValue | React 18 用于延迟某些值更新,避免频繁重渲染 |
useSyncExternalStore | 订阅外部 store,用于状态库如 Redux 的订阅优化 |
useDebugValue | 自定义 Hook 中用于调试显示的值(如开发者工具展示) |
next 动态加载组件
1import dynamic from 'next/dynamic'; 2 3// 动态加载组件,设置loading骨架屏,ssr默认是开启的 4const ClientLightbox = dynamic(() => import('./ClientLightbox'), { 5 // ssr: false, // 如果组件只能在客户端渲染,取消注释此行 6 loading: () => <SkeletonPlaceholder />, // 加载时显示骨架屏 7}); 8 9export default function Page() { 10 return ( 11 <div> 12 <h1>页面标题</h1> 13 <ClientLightbox /> 14 </div> 15 ); 16} 17