# 1. React 介绍

React 是一个用于构建用户界面的 JavaScript ​​库​​(而非框架),由 Facebook 开发并开源。

# 一、什么是 JSX?

JSX (JavaScript XML) 是 JavaScript 的语法扩展,它允许开发者在 JavaScript 代码中编写类似 HTML 的结构。

class App extends React.Component {
  render() {
    return(
      <div>
        <h1>{'Welcome to React world!'}</h1>
      </div>
    )
  }
}
1
2
3
4
5
6
7
8
9

以上示例 render 方法中的 JSX 将会被转换为以下内容:

React.createElement("div", null, React.createElement(
  "h1", null, 'Welcome to React world!'));
1
2

# 二、React开发是否必须使用 JSX?

答案是不必须​​,但强烈推荐使用。以下是详细分析

# 不使用 JSX 的替代方案

使用纯 JavaScript 调用 React.createElement:

// 用 JSX 编写的组件
function HelloJSX() {
  return <div className="greeting">Hello</div>;
}

// 等效的不使用 JSX 写法
function HelloNoJSX() {
  return React.createElement(
    'div',
    { className: 'greeting' },
    'Hello'
  );
}
1
2
3
4
5
6
7
8
9
10
11
12
13

# 2.React 创建组件的方式

# 一、函数组件(Function Components)​

  • 简介​​:通过 JavaScript 函数定义的组件,是 React 推荐的主流方式(尤其是配合 Hooks 后)。
  • ​​特点​​:
    • 简洁,适合无状态或使用 Hooks 管理状态的组件。
    • 性能较好(无类组件的实例化开销)。
function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}
// 或使用箭头函数
const Welcome = (props) => <h1>Hello, {props.name}</h1>;
1
2
3
4
5

# 二、​类组件(Class Components)​

  • ​简介​​:通过 ES6 类定义的组件,是 React 早期的主要方式。
  • ​特点​​:
    • 可以维护自身状态(this.state)和生命周期方法。
    • 需要显式绑定事件处理函数的 this。
class Welcome extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}
1
2
3
4
5

# 三. ​​React Hooks(函数组件的增强)​

  • 简介​​:Hooks 允许函数组件使用状态和其他 React 特性(如生命周期)。
  • 常用 Hooks​​:
    • useState:管理组件状态。
    • useEffect:处理副作用(替代生命周期)。
import { useState } from 'react';
function Counter() {
  const [count, setCount] = useState(0);
  return (
    <button onClick={() => setCount(count + 1)}>
      Clicked {count} times
    </button>
  );
}
1
2
3
4
5
6
7
8
9

# 四. 高阶组件(HOC, Higher-Order Component)​

  • ​简介​​:一种复用组件逻辑的模式(接收组件,返回增强后的组件)。
  • ​​用途​​:例如权限控制、日志记录等跨组件逻辑。
  • 核心特点
    • ​不修改原组件​​,而是通过组合的方式增强功能
    • ​​透传 props​​(需要手动处理 props 传递)
    • 通常以 with 开头命名(如 withRouter)
function withLoading(WrappedComponent) {
  return function(props) {
    const [loading, setLoading] = useState(true);
    useEffect(() => {
      // 模拟数据加载
      setTimeout(() => setLoading(false), 1000);
    }, []);
    return loading ? <div>Loading...</div> : <WrappedComponent {...props} />;
  };
}

// 使用
const EnhancedComponent = withLoading(MyComponent);
1
2
3
4
5
6
7
8
9
10
11
12
13

# 五. Render Props​

  • ​​简介​​:组件通过一个名为 render(或其他名称)的 prop 接收渲染逻辑函数。
  • ​注意事项
    • ​可以使用任意名称​​,不一定是 render(如 children)
    • ​注意性能优化​​,避免不必要的重新渲染
    • ​与 React.memo 结合使用时需小心​
<DataProvider render={data => (
  <ChildComponent data={data} />
)}/>
1
2
3

实现示例

function MouseTracker(props) {
  const [position, setPosition] = useState({ x: 0, y: 0 });

  const handleMouseMove = (e) => {
    setPosition({ x: e.clientX, y: e.clientY });
  };

  return (
    <div onMouseMove={handleMouseMove}>
      {props.render(position)}
    </div>
  );
}

// 使用
<MouseTracker render={({x, y}) => (
  <div>
    Mouse position: {x}, {y}
  </div>
)}/>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# 如何选择哪种方式创建组件?

  • 优先使用函数组件 + Hooks​​:现代 React 开发的主流方式,代码更简洁。
  • ​​类组件​​:仅在维护旧项目或需要 Error Boundaries 时使用。
  • ​高阶组件/Render Props​​:复杂逻辑复用时考虑。
  • 随着 React Hooks 的普及,许多 HOC 和 Render Props 的场景可以用自定义 Hook 更简洁地实现:
// 自定义 Hook 替代 MouseTracker 的 Render Props
function useMousePosition() {
  const [position, setPosition] = useState({ x: 0, y: 0 });
  useEffect(() => {
    const handleMouseMove = (e) => {
      setPosition({ x: e.clientX, y: e.clientY });
    };
    window.addEventListener('mousemove', handleMouseMove);
    return () => window.removeEventListener('mousemove', handleMouseMove);
  }, []);
  return position;
}

// 使用
function App() {
  const { x, y } = useMousePosition();
  return <div>Mouse position: {x}, {y}</div>;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 3.条件渲染

# 一、if/else 语句

function Greeting({ isLoggedIn }) {
  if (isLoggedIn) {
    return <UserGreeting />;
  } else {
    return <GuestGreeting />;
  }
}
1
2
3
4
5
6
7
  • 特点​​:
    • 适合较大的条件分支
    • 不能在 JSX 中直接使用

# 二、三元运算符

function Greeting({ isLoggedIn }) {
  return (
    <div>
      {isLoggedIn ? <UserGreeting /> : <GuestGreeting />}
      <p>You are {isLoggedIn ? 'logged in' : 'a guest'}.</p>
    </div>
  );
}
1
2
3
4
5
6
7
8
  • 特点​​:
    • 可以直接嵌入 JSX
    • 嵌套过多会降低可读性

# 三、逻辑与运算符 (&&)

function Mailbox({ unreadMessages }) {
  return (
    <div>
      <h1>Hello!</h1>
      {unreadMessages.length > 0 && (
        <h2>You have {unreadMessages.length} unread messages.</h2>
      )}
    </div>
  );
}
1
2
3
4
5
6
7
8
9
10
  • 特点​​:
    • 注意:左侧表达式必须为布尔值(避免 0 && <Component /> 的问题)
    • 嵌套过多会降低可读性

# 四. 立即执行函数 (IIFE)

function Greeting({ user }) {
  return (
    <div>
      {(() => {
        if (user.firstName) {
          return <p>Hello, {user.firstName}!</p>;
        } else if (user.isGuest) {
          return <p>Welcome, Guest!</p>;
        } else {
          return <p>Please sign in.</p>;
        }
      })()}
    </div>
  );
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  • 特点​​:
    • 可读性较差
    • 现代 React 中较少使用

# 4.在哪个生命周期发起Ajax请求

# 一、类组件

# 1. componentDidMount​​ (最常用)

  • 组件挂载完成后立即调用
  • 适合初始数据加载
class MyComponent extends React.Component {
  componentDidMount() {
    fetch('/api/data')
      .then(res => res.json())
      .then(data => this.setState({ data }));
  }
}
1
2
3
4
5
6
7

# 2. componentDidUpdate​​ (用于props/state变化时)

  • 在更新发生后立即调用
  • 适合根据props变化重新获取数据
class MyComponent extends React.Component {
  componentDidUpdate(prevProps) {
    if (this.props.userId !== prevProps.userId) {
      fetch(`/api/users/${this.props.userId}`)
        .then(res => res.json())
        .then(user => this.setState({ user }));
    }
  }
}
1
2
3
4
5
6
7
8
9

# 二、函数组件,使用 useEffect Hook

# 1. ​​初始数据加载​​ (相当于componentDidMount)

function MyComponent() {
  const [data, setData] = useState(null);
  
  useEffect(() => {
    fetch('/api/data')
      .then(res => res.json())
      .then(setData);
  }, []); // 空依赖数组表示只在挂载时运行一次
}
1
2
3
4
5
6
7
8
9

# 2. props变化时重新获取数据​​ (相当于componentDidUpdate)

function UserProfile({ userId }) {
  const [user, setUser] = useState(null);
  
  useEffect(() => {
    fetch(`/api/users/${userId}`)
      .then(res => res.json())
      .then(setUser);
  }, [userId]); // userId变化时重新运行
}
1
2
3
4
5
6
7
8
9

# 5.最外层可以不包裹 div 标签

# 一、React Fragments(推荐方案)

React 16.2+ 提供了专门的 Fragment 语法:

// 简写语法(最常用)
const Component = () => (
  <>
    <ChildA />
    <ChildB />
    <ChildC />
  </>
);
// 完整写法
const Component = () => (
  <React.Fragment>
    <ChildA />
    <ChildB />
    <ChildC />
  </React.Fragment>
);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 二、数组返回(React 16+)

const Component = () => [
  <ChildA key="a" />,
  <ChildB key="b" />,
  <ChildC key="c" />
];
1
2
3
4
5
  • 注意​​:
    • 必须为每个元素添加 key
    • 某些情况下样式处理可能更复杂

# 6.setState 传参的方式

# 一. 类组件中的 setState

# 1. 基本用法 - 直接传入新状态对象

this.setState({ count: 1 });
1

# 2. 基于前一个状态的更新 - 传入函数

this.setState((prevState, props) => {
  // prevState 是之前的状态
  // props 是当前的 props
  return { count: prevState.count + 1 };
});
1
2
3
4
5
  • 使用场景​​:
    • 当新状态依赖于前一个状态时
    • 避免状态更新合并导致的错误

# 3. 第二个参数 - 回调函数

this.setState(
  { count: 1 },
  () => {
    // 状态更新完成后的回调
    console.log('状态已更新', this.state.count);
  }
);
1
2
3
4
5
6
7

# 二. 函数组件中的 useState

# 1. 基本用法 - 直接传入新值

const [count, setCount] = useState(0);
// 直接设置新值
setCount(1);
1
2
3

# 2. 基于前一个状态的更新 - 传入函数

setCount(prevCount => prevCount + 1);
1
  • 使用场景​​:
    • 当新状态依赖于前一个状态时
    • 在闭包中获取最新状态值

# 三. 对象状态更新的注意事项

# 1. 合并更新(类组件)

state = { name: 'John', age: 25 };
// 只更新 age,name 保持不变
this.setState({ age: 26 });s
1
2
3

# 2. 展开运算符更新对象(函数组件)

const [user, setUser] = useState({ name: 'John', age: 25 });
// 更新 age,保留其他属性
setUser(prevUser => ({ ...prevUser, age: 26 }));
1
2
3

# 五. 常见问题解决方案

# 1:连续多次 setState 不生效

// 函数组件中
const [count, setCount] = useState(0);
const handleClick = () => {
  setCount(count + 1);
  setCount(count + 1); // 不会累加
};
1
2
3
4
5
6

正确解决方案​​:

const handleClick = () => {
  setCount(prev => prev + 1);
  setCount(prev => prev + 1); // 现在会正确累加
};
1
2
3
4

# 7.setState同步还是异步

# 正常情况下:setState 是异步的​

在 ​​React 的​生命周期或合成事件​(如 onClick、onChange)​​ 中调用 setState 时,React 会​​批量处理更新​​​,表现为​​异步​​行为:

class Example extends React.Component {
  state = { count: 0 };

  handleClick = () => {
    console.log("Before setState:", this.state.count); // 0
    this.setState({ count: this.state.count + 1 });
    console.log("After setState:", this.state.count); // 0(仍未更新)
  };

  render() {
    return <button onClick={this.handleClick}>Click</button>;
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
  • 原因​​:React 会合并多个 setState 调用,并在稍后的某个时间点统一更新状态(​​批量更新​​)。
  • React 为什么设计为异步?​​
    • ​性能优化​​:避免频繁渲染,合并多个 setState 减少不必要的 re-render。
    • ​​保证一致性​​:在事件处理中,确保所有 setState 调用完成后才触发渲染,避免中间状态不一致。

# 特殊情况:setState 是同步的​

在某些情况下,setState 会​​立即执行​​,表现为同步行为:

  • 在React 16+的Fiber架构中:
    • 即使同步更新也不会立即刷新DOM
    • 但state的值会立即更新(可同步获取)
    • DOM更新仍然会被合理调度

# ​(1) 在 setTimeout、Promise、原生DOM事件中调用​

class Example extends React.Component {
  state = { count: 0 };

  componentDidMount() {
    // 原生事件 - 同步更新
    document.getElementById('btn').addEventListener('click', () => {
      this.setState({ count: this.state.count + 1 });
      console.log(this.state.count); // 输出新值
    });
  }

  handleClick = () => {
    setTimeout(() => {
      console.log("Before setState:", this.state.count); // 0
      this.setState({ count: this.state.count + 1 });
      console.log("After setState:", this.state.count); // 1(同步更新)
    }, 0);
  };

  render() {
    return <button onClick={this.handleClick}>Click</button>;
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
  • ​原因​​:setTimeout 脱离了 React 的批处理机制,导致 setState 立即执行。

# (2) 使用 ReactDOM.flushSync 强制同步更新

import { flushSync } from "react-dom";

class Example extends React.Component {
  state = { count: 0 };

  handleClick = () => {
    flushSync(() => {
      this.setState({ count: 1 });
    });
    console.log("After flushSync:", this.state.count); // 1(同步)
  };

  render() {
    return <button onClick={this.handleClick}>Click</button>;
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
  • 用途​​:在需要立即获取最新状态时使用(如动画、测量布局)。

# 8.获取 setState 更新后的值

# 一、class组件

# 1. setState 回调函数​(推荐)

this.setState(
  { count: this.state.count + 1 },
  () => {
    // 在这里可以获取更新后的state
    console.log('更新后的值:', this.state.count);
    // 可以在这里执行依赖于新state的操作
  }
);
1
2
3
4
5
6
7
8

# 2. 使用componentDidUpdate生命周期

componentDidUpdate(prevProps, prevState) {
  if (prevState.count !== this.state.count) {
    console.log('count已更新:', this.state.count);
    // 可以在这里执行副作用操作
  }
}
1
2
3
4
5
6

# 3. 使用函数式setState(确保基于最新状态)

this.setState(prevState => {
  const newCount = prevState.count + 1;
  // 这里可以立即使用新值进行计算
  console.log('计算中的新值:', newCount);
  return { count: newCount };
});
1
2
3
4
5
6

# 二、函数组件

# 1. 使用useEffect Hook

import { useState, useEffect } from 'react';
function Counter() {
  const [count, setCount] = useState(0);
  useEffect(() => {
    console.log('count已更新:', count);
    // 这里可以执行副作用操作
  }, [count]); // 依赖count变化
  return (
    <button onClick={() => setCount(c => c + 1)}>
      点击我: {count}
    </button>
  );
}
1
2
3
4
5
6
7
8
9
10
11
12
13

# 2. 使用useState的函数式更新

const [count, setCount] = useState(0);

const increment = () => {
  setCount(prevCount => {
    const newCount = prevCount + 1;
    console.log('新值:', newCount); // 立即获取新值
    return newCount;
  });
};
1
2
3
4
5
6
7
8
9

# 三、强制同步获取更新后的值(不推荐)

# 1. 使用ReactDOM.flushSync(React 18+)

import { flushSync } from 'react-dom';

// 强制同步更新
flushSync(() => {
  this.setState({ count: 42 });
});
console.log(this.state.count); // 立即获取新值
1
2
3
4
5
6
7

# 2. 在setTimeout/Promise/原生事件中

setTimeout(() => {
  this.setState({ count: 100 });
  console.log(this.state.count); // 同步获取
}, 0);
1
2
3
4

# 9.React Router(V6)

# 一、核心概念​

# 1. 路由类型

生命周期阶段 选项式 API
BrowserRouter 基于 HTML5 History API(推荐)
HashRouter 基于 URL Hash(兼容旧浏览器)
MemoryRouter 测试或非浏览器环境(如 React Native)

# 2. 基础组件​

组件​ ​ ​作用​
<Routes> 路由容器(v6 新增,替代 <Switch>
<Route> 定义路由规则
<Link> / <NavLink> 导航链接(NavLink 支持高亮)
<Outlet> 嵌套路由的占位符
useNavigate 编程式导航(替代 useHistory)
useParams 获取动态路由参数

# 二、基本示例​

import { BrowserRouter, Routes, Route, Link } from "react-router-dom";
function App() {
  return (
    <BrowserRouter>
      <nav>
        <Link to="/">Home</Link>
        <Link to="/about">About</Link>
      </nav>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/about" element={<About />} />
        <Route path="*" element={<NotFound />} /> {/* 404 页面 */}
      </Routes>
    </BrowserRouter>
  );
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 三、嵌套路由​

# 1. 动态路由参数​

<Route path="/users/:id" element={<UserDetail />} />
// 在组件中获取参数
function UserDetail() {
  const { id } = useParams();
  return <div>User ID: {id}</div>;
}
1
2
3
4
5
6

# 2. 配置嵌套路由​

<Route path="/dashboard" element={<Dashboard />}>
  <Route path="profile" element={<Profile />} />
  <Route path="settings" element={<Settings />} />
</Route>
// Dashboard.jsx
function Dashboard() {
  return (
    <div>
      <h1>Dashboard</h1>
      <nav>
        <Link to="profile">Profile</Link>
        <Link to="settings">Settings</Link>
      </nav>
      <Outlet /> {/* 子路由将渲染在这里 */}
    </div>
  );
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 3. 默认嵌套路由​

<Route path="/dashboard" element={<Dashboard />}>
  <Route index element={<DashboardHome />} /> {/* 默认子路由 */}
  <Route path="profile" element={<Profile />} />
</Route>
1
2
3
4

# 四、编程式导航

# 1. 使用 useNavigate

import { useNavigate } from "react-router-dom";
function Login() {
  const navigate = useNavigate();
  const handleLogin = () => {
    navigate("/dashboard", { replace: true }); // replace 替换当前历史记录
  };
  return <button onClick={handleLogin}>Login</button>;
}
1
2
3
4
5
6
7
8

# 2. 导航传参​

// 传递 state
navigate("/user", { state: { from: "/login" } });
// 接收 state
const location = useLocation();
console.log(location.state?.from); // "/login"
1
2
3
4
5

# 五、路由守卫(权限控制)​

# 1. 封装 ProtectedRoute​

function ProtectedRoute({ children }) {
  const { user } = useAuth(); // 假设有权限钩子
  const location = useLocation();
  if (!user) {
    return <Navigate to="/login" state={{ from: location }} replace />;
  }
  return children;
}
// 使用
<Route
  path="/admin"
  element={
    <ProtectedRoute>
      <AdminPanel />
    </ProtectedRoute>
  }
/>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 2. 全局路由拦截​

function RouterWrapper() {
  const location = useLocation();
  const { isAuthenticated } = useAuth();
  if (!isAuthenticated && location.pathname !== "/login") {
    return <Navigate to="/login" state={{ from: location }} replace />;
  }
  return <Outlet />;
}

// 在根路由中使用
<Route element={<RouterWrapper />}>
  <Route path="/" element={<Home />} />
  <Route path="/admin" element={<AdminPanel />} />
</Route>
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 常见问题​

# 1. 如何实现路由懒加载?​

const LazyComponent = React.lazy(() => import("./Component"));

<Route
  path="/lazy"
  element={
    <React.Suspense fallback={<Spinner />}>
      <LazyComponent />
    </React.Suspense>
  }
/>
1
2
3
4
5
6
7
8
9
10

# 2. 如何监听路由变化?​

useEffect(() => {
  const unlisten = navigation.listen((location) => {
    console.log("Route changed to:", location.pathname);
  });
  return () => unlisten(); // 清理监听
}, [navigation]);
1
2
3
4
5
6

# 10.高阶组件(HOC)

高阶组件(Higher-Order Component,HOC)是 React 中用于​​复用组件逻辑​​的高级技术,它本质上是一个函数,接收一个组件并返回一个新的增强组件。

# 一、核心概念

# 1. 基本结构

const EnhancedComponent = higherOrderComponent(WrappedComponent);
1

# 2. 类比高阶函数

就像高阶函数接收/返回函数一样,HOC 接收/返回组件:

// 高阶函数示例
const twice = f => x => f(f(x));
// 高阶组件示例
const withLogging = Component => props => {
  console.log('Rendered:', Component.name);
  return <Component {...props} />;
};
1
2
3
4
5
6
7

# 二、实现方式

# 1. 属性代理(Props Proxy)

// 最常见的 HOC 形式:
function withExtraProps(WrappedComponent) {
  return function EnhancedComponent(props) {
    const extraProps = { user: currentUser };
    return <WrappedComponent {...props} {...extraProps} />;
  };
}
1
2
3
4
5
6
7

# 2. 继承反转(Inheritance Inversion)

// 通过继承操纵渲染:
function withTheme(WrappedComponent) {
  return class extends WrappedComponent {
    render() {
      return (
        <div style={{ color: this.props.themeColor }}>
          {super.render()}
        </div>
      );
    }
  };
}
1
2
3
4
5
6
7
8
9
10
11
12

# 三、常见应用场景

# 1. 属性增强

const withUser = Component => props => (
  <Component {...props} user={getCurrentUser()} />
);
1
2
3

# 2. 逻辑复用

const withLoading = Component => props => 
props.isLoading ? <Spinner /> : <Component {...props} />;
1
2

# 3. 状态抽象

const withToggle = Component => {
  return class extends React.Component {
    state = { isOn: false };
    toggle = () => this.setState({ isOn: !this.state.isOn });
    
    render() {
      return (
        <Component 
          {...this.props} 
          isOn={this.state.isOn} 
          toggle={this.toggle}
        />
      );
    }
  };
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
lastUpdate: 6/4/2025, 11:06:17 PM