《React开发者的超能力手册:从组件化到Hooks的实战进化指南》

120 阅读4分钟

npm run build

可以将public的代码打包到build文件夹下面

  • 单页应用:整个项目只有唯一的一个 html 文件,所有页面都做成组件的样子,被添加到这个 html 文件中进行渲染

前言:每个开发者都该知道的React生存指南

想象一下:你刚接手一个新项目,发现代码库里全是类组件,维护起来像在解鲁班锁。这时候你可能会想:"要是能用函数组件+Hooks重构该多好!"——别担心,这本指南就是你的React生存手册,帮你从类组件的泥潭里爬出来,拥抱现代React的最佳实践。

组件化思维:把UI拆成乐高积木

组件

  1. class 组件
  2. function 组件

建议
除非维护旧项目,否则优先用函数组件。就像用智能手机替代老式功能机——更轻便、更智能

hooks(钩子函数)

  • 由 react 官方封装好的一系列函数,它们的用法和作用
  1. useState 状态 定义一个响应式的变量,并提供专门的方法修改这个变量值

  2. useEffec--副作用函数(可以放异步函数)(附加作用)

    1. 组件每次加载(挂载),都会触发
    2. useEffect(fn, []),第二个参数是一个空数组时,只会在初次渲染(挂载)时触发
    3. useEffect(fn, [state]),第二个参数是一个数组,数组中是一个 state 时,该变量每次修改值时,都会带来 useEfect 的重新触发
    4. useEffect 第一个参数是函数,该函数内部会返回出来一个新的函数,新函数会在当前整个组件不展示(卸载)时触发

import { useEffect, useState } from "react";

async function queryData() {
  const data = await new Promise((resolve) => {setTimeout(() => resolve(100), 1000);});
  return data;
}
function App() {

  const [num, setNum] = useState(1);
  // 当num只改变时,浏览器的useLayoutEffect中的effect函数执行完毕再渲染,effect会阻塞很久,超过500毫秒会导致掉帧
  useEffect(() => {
    console.log("useLayoutEffect");
    queryData().then((data) => {
    setNum(data);
    });
  }, [num]);
  return (
    <div>
      <button
        onClick={() => {
          setNum(num + 1);
        }}
      >
        {num}
      </button>
    </div>
  );
}
export default App;
  1. useLayoutEffect 中的 effect(这里的回调函数)函数作为同步函数来执行,useEffect作为异步来执行
import { useLayoutEffect, useState } from "react";


function App() {

  const [num, setNum] = useState(1);
 
  useLayoutEffect(() => {
    setNum(100);
  }, [num]);
  return (
    <div>
      <button
        onClick={() => {
          setNum(num + 1);
        }}
      >
        {num}
      </button>
    </div>
  );
}
export default App;

%E5%B1%8F%E5%B9%95%E5%BD%95%E5%88%B6%202025-07-09%20193527_converted.gif 好处是可以防止闪动,坏处是这里useLayoutEffect的执行时间超过500ms(长任务)会阻塞后续代码的渲染,造成掉帧。

  1. useReducer--当修改 state 的逻辑比较复杂时,用 useReducer(相比较于useState的优点)
    1. 传入的 Reducer 函数中不能直接修改原 state,必须返回一个新的对象(缺点)
    2. useReducer +immer(第三方工具)

const [res,dispatch]=useReducer(reducer,{result:0}), useReducer接受一个函数体和一个初始值, 这里useReducer接受的第一个参数是一个函数,第二个参数是一个对象,调用dispatch传入一个对象作为useReducer第一个参数(函数体中),而参数(函数体)里面有两个参数,第一个参数是useReducer里面的第二个参数,函数的第二个参数是dispatch传入的参数 (对象)

import { useReducer } from "react";
import {produce} from 'immer'

function Reducer(state, action){
  switch(action.type){
    case "add":
      // return {
      //   result:state.result+action.num
      // }
      return produce(state,(state)=>{
        state.result+=action.num
      })
      case "minus":
      return {
        result:state.result-action.num
      }
    default:
      return state
  }
}
function App() {
//useReducer接收的第二个的参数作为Reducer的第一个参数,
//dispatch接收的参数作为Reducer的第二个参数
const [res,dispatch] = useReducer(Reducer,{result:0,a:{b:{c:1}}})
  return (
    <div>
     <h3>{res.result}</h3>
     <button onClick={() => dispatch({type:"add",num:2})}>+</button>
     <button onClick={() => dispatch({type:"minus",num:2})}>-</button>

    </div>
  );
}
export default App;

produce()帮我们对原对象进行深拷贝

  1. useRef--获取 DOM 结构

在标签上添加ref={xxx}来获取

  1. useContext--跨多层组件进行数据传递(数据传递也可以通过标签添加属性逐级传递) 缺点只能父往子方向传递,不能子向父传递(可以建立仓库redux实现任意组件传递)
import { useContext,createContext } from "react"
function Child1(){
  const count = useContext(AppContext)
  return (
        <div>
        <h2>子组件{count}</h2>
            <Child2/>
        </div>
    )
}

function Child2(){
const count = useContext(AppContext)
  return(<h3>孙子组件{count}</h3>)

}


const AppContext = createContext()
function App(){
    const num = '数据'
    return (
        <AppContext.Provider value={num}>
            <h1>父组件</h1>
         <Child1/>
        </AppContext.Provider>
    )
}
export default App

总结:

🔹 组件化思维

  • 函数组件 vs 类组件的选择原则
  • 组件拆分的"单一职责"原则(就像厨房里刀、锅、炉具各司其职)

🔹 Hooks 实战技巧

  • useState 管理简单状态(计数器、模态框开关)
  • useEffect 处理数据请求(学会用清理函数避免内存泄漏)
  • useLayoutEffect 优化视觉更新(告别恼人的闪屏问题)

🔹 状态管理进阶

  • 复杂逻辑用 useReducer(就像给状态机编写操作手册)
  • Immer 库让不可变数据操作变得简单(深拷贝从此不再头疼)

🔹 跨组件通信

  • useContext 实现多层传参(就像在公司内部发通知)