一个新的架构设计必然是为了解决旧架构设计的痛点和问题,那我们就需要看旧的架构体系中存在哪些问题。
1. Class架构设计问题
1.1. 代码复用困难
在 Class 组件中,共享状态逻辑很麻烦,只能通过 高阶组件(HOC) 或 Render Props,代码变得复杂且难以维护。
Class 组件共享逻辑问题
            
            
              scala
              
              
            
          
          // 创建一个高阶组件(HOC)来共享逻辑
function withMousePosition(WrappedComponent) {
  return class extends React.Component {
    state = { x: 0, y: 0 };
    componentDidMount() {
      window.addEventListener("mousemove", this.handleMouseMove);
    }
    componentWillUnmount() {
      window.removeEventListener("mousemove", this.handleMouseMove);
    }
    handleMouseMove = (event) => {
      this.setState({ x: event.clientX, y: event.clientY });
    };
    render() {
      return <WrappedComponent {...this.props} mouse={this.state} />;
    }
  };
}
// 使用高阶组件
class MyComponent extends React.Component {
  render() {
    return <h1>Mouse Position: {this.props.mouse.x}, {this.props.mouse.y}</h1>;
  }
}
export default withMousePosition(MyComponent);
        问题:
- 需要写一个 HOC,逻辑不直观
 - 逻辑拆分困难,复用时组件层级变多
 - 组件嵌套层级变深,导致 "Wrapper Hell"
 
1.2. Class 组件 this 指向困扰
Class 组件使用 this.setState 更新状态,导致 this 需要手动绑定,容易出错。
this 绑定问题
            
            
              kotlin
              
              
            
          
          class Counter extends React.Component {
  constructor(props) {
    super(props);
    this.state = { count: 0 };
    this.increment = this.increment.bind(this); // 必须绑定
  }
  increment() {
    this.setState({ count: this.state.count + 1 });
  }
  render() {
    return <button onClick={this.increment}>Click Me: {this.state.count}</button>;
  }
}
        问题:
this需要手动绑定,容易忘记,导致undefined错误- 可以使用箭头函数 
increment = () => {},但语法冗长 
1.3. 生命周期方法不清晰
Class 组件的生命周期函数 componentDidMount、componentDidUpdate、componentWillUnmount职责混杂,一个生命周期函数可能涉及多个逻辑,导致代码难以管理。
生命周期混乱
            
            
              javascript
              
              
            
          
          class Example extends React.Component {
  componentDidMount() {
    this.fetchData();
    window.addEventListener("resize", this.handleResize);
  }
  componentWillUnmount() {
    window.removeEventListener("resize", this.handleResize);
  }
  fetchData() {
    // 获取数据
  }
  handleResize = () => {
    // 处理窗口大小变化
  };
  render() {
    return <div>Example Component</div>;
  }
}
        问题:
componentDidMount里有多个逻辑(数据请求、监听事件)componentWillUnmount需要手动清理副作用- 代码难以拆分和复用
 
2. Hooks 解决了这些问题
Hooks 让我们在 函数组件 里使用状态和生命周期逻辑,避免 this 问题,逻辑更清晰,代码更简洁。
2.1. useCustom Hook 解决状态存储和复用
自定义 Hook 让我们可以封装可复用的逻辑,不需要 HOC 也能复用状态。
            
            
              javascript
              
              
            
          
          import { useState, useEffect } from "react";
// 自定义 Hook
function useMousePosition() {
  const [mouse, setMouse] = useState({ x: 0, y: 0 });
  useEffect(() => {
    const updateMouse = (e) => setMouse({ x: e.clientX, y: e.clientY });
    window.addEventListener("mousemove", updateMouse);
    return () => {
      window.removeEventListener("mousemove", updateMouse);
    };
  }, []);
  return mouse;
}
// 组件中使用
function MyComponent() {
  const mouse = useMousePosition();
  return <h1>Mouse Position: {mouse.x}, {mouse.y}</h1>;
}
        ✔ 优势:
- 逻辑封装成 
useMousePosition,可复用 - 组件结构扁平,避免 "Wrapper Hell"
 - 逻辑更清晰
 
2.2. useState 让函数组件有状态
            
            
              javascript
              
              
            
          
          import React, { useState } from "react";
function Counter() {
  const [count, setCount] = useState(0);
  return <button onClick={() => setCount(count + 1)}>Click Me: {count}</button>;
}
        ✔ 优势:
- 去掉了 class,函数式更简单
 - 不用手动绑定 
this,不会有this相关 bug 
2.3. useEffect 让函数组件有生命周期能力
            
            
              javascript
              
              
            
          
          import React, { useState, useEffect } from "react";
function Example() {
  const [width, setWidth] = useState(window.innerWidth);
  useEffect(() => {
    const handleResize = () => setWidth(window.innerWidth);
    window.addEventListener("resize", handleResize);
    return () => {
      window.removeEventListener("resize", handleResize); // 清理副作用
    };
  }, []);
  return <div>Window Width: {width}</div>;
}
        ✔ 优势:
useEffect统一管理副作用,不再需要componentDidMount和componentWillUnmount- 避免了生命周期拆分困难的问题
 
3. Hooks是如何解决以上问题的呢?
3.1. Hooks 的状态存储和复用
- Class 组件状态存储
 
在 Class 组件 中, state 存在于 组件实例 上:
            
            
              scala
              
              
            
          
          class Counter extends React.Component {
  constructor() {
    super();
    this.state = { count: 0 };
  }
}
        React 内部会创建 Counter 组件的 实例对象:
            
            
              ini
              
              
            
          
          const instance = new Counter();
        - 
- 状态存储在实例 ( 
instance.state) 上 - this 让 state 成为组件的属性
 - 复用逻辑必须靠继承、HOC 或者 Render Props
 
 - 状态存储在实例 ( 
 
- Hooks 组件状态存储
 
Hooks 不使用类实例存储状态 ,而是存储在 React Fiber 的链表中:
            
            
              scss
              
              
            
          
          function Counter() {
  const [count, setCount] = useState(0);
}
        底层实现:
            
            
              ini
              
              
            
          
          let hookIndex = 0;
const hooks = [];
function useState(initialValue) {
  if (!hooks[hookIndex]) {
    hooks[hookIndex] = initialValue;
  }
  const setState = (newValue) => {
    hooks[hookIndex] = newValue;
    render(); // 重新渲染
  };
  return [hooks[hookIndex++], setState];
}
        Hooks 组件是如何存储状态的?
- 
- 每个函数组件都有一个 
hooks数组 ,存储useState的状态 - 每次 
useState都会从hooks数组里取值 - 组件重新渲染时, 
useState仍然能找到之前的值 
 - 每个函数组件都有一个 
 
这样就不需要 Class 组件的 this.state 了。
3.2. 为什么 Hooks 没有 this 困扰?
- Hooks 是基于闭包的,而不是基于类实例
 - 每次渲染时,Hooks 创建一个全新的作用域
 useState返回的是 局部变量,而不是实例属性 ,不需要this
底层实现分析
在 React 内部,Class 组件的 state 是绑定在实例上的:
            
            
              scala
              
              
            
          
          class Counter extends React.Component {
  constructor() {
    super();
    this.state = { count: 0 };
  }
}
        而 Hooks 直接使用一个数组或链表来存储状态 ,避免 this 的问题:
            
            
              scss
              
              
            
          
          function Counter() {
  const [count, setCount] = useState(0); // 这里 count 只是一个变量,不属于实例
}
        在 函数组件重新执行时,它创建了一个新的作用域 ,count 变量 不会丢失 ,因为 React 通过一个 隐藏的数组存储每次渲染的状态。
3.3. 工作原理
Hooks 的运行机制 依赖 React Fiber 架构 ,它通过 链表结构管理每次组件的状态。
3.3.1. Fiber 是什么?
Fiber 是 React 内部维护的 组件更新数据结构 ,它类似于 链表结构:
            
            
              kotlin
              
              
            
          
          const fiber = {
  type: Counter, // 组件类型
  stateNode: null, // 组件实例
  child: null, // 子 Fiber
  sibling: null, // 兄弟 Fiber
  return: null, // 父 Fiber
  hooks: [], // 存储 useState/useEffect 的状态
};
        Fiber 中的 hooks 数组存储组件的状态!
3.3.2. Hooks 在 Fiber 中的存储
            
            
              ini
              
              
            
          
          function useState(initialState) {
  const oldHook = currentFiber?.hooks[hookIndex]; // 取上次渲染的状态
  const hook = { state: oldHook ? oldHook.state : initialState };
  
  function setState(newState) {
    hook.state = newState;
    render(); // 触发更新
  }
  currentFiber.hooks[hookIndex++] = hook; // 存入 Fiber
  return [hook.state, setState];
}
        存储过程:
- React 在 Fiber 结构中存储 
useState的状态 - 每次渲染时,Hooks 通过 
hookIndex取回上次的状态 setState更新状态后,React 触发 重新渲染
4. Hooks 必须在最顶层调用?
- React 按顺序存储 Hooks
 - 如果条件变化,Hook 调用顺序会错乱
 - React 通过 
hookIndex读取状态,顺序变了就会报错