react 概念

作者:Thomas che12 分钟阅读
浏览量:加载中...
#react

常用HOOK

react 概念

开发理念


父组件(或页面、布局)默认是服务端组件(Server Component),它负责数据获取、SEO、渲染静态内容,性能更好。


子组件只在需要交互、状态管理、浏览器 API 等客户端功能时,单独加 "use client",让它成为客户端组件(Client Component)。


这样父组件在服务器渲染,子组件在客户端渲染,二者共存,互不冲突。


核心概念

模块内容
JSXJSX 是语法糖,必须包裹单一元素,支持表达式,不支持语句;可以使用 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.memouseCallbackuseMemo
避免虚渲染条件渲染提前返回(如 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
滚动监听useScrollIntersectionObserver
星星移动背景自定义 canvas or DOM 随鼠标偏移实现
Loading 状态Skeleton、Spinner、Suspense fallback

常见陷阱和误区(避免踩坑)


陷阱说明
setState 不立即更新是异步的,依赖上一次值时要用函数式 setState(prev => ...)
useEffect 忘记依赖会造成 stale 问题或死循环
多次请求未清理请求未取消就卸载页面,导致内存泄漏(需加 AbortController
子组件重新渲染频繁函数 props 每次重建(用 useCallback 优化)
依赖外部变量未 memouseEffect 中使用的外部对象或函数未作为依赖传入

调试技巧(调试工具 + 开发习惯)


工具作用
React DevTools查看组件树、props、hooks、性能分析等
Profiler性能分析,找出渲染瓶颈
console.trace()查看状态变化调用路径
状态打印利用 useEffect(() => console.log(state), [state])

推荐常用库


类别工具
表单管理react-hook-formformik
状态管理zustandreduxjotai
数据请求swrreact-query
动画framer-motion
响应式布局tailwindcss
组件库shadcn/uichakra-uiantd



组件用法


函数式写法


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说明
useLayoutEffectuseEffect 类似,但同步执行于 DOM 更新后(适合测量 DOM 尺寸)
useImperativeHandleforwardRef 一起使用,自定义对外暴露的 ref 方法
useId生成唯一 ID,适用于服务端渲染场景
useTransitionReact 18 用于异步 UI 更新,避免阻塞主渲染
useDeferredValueReact 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