# 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 创建组件的方式

# 通过 JS 函数 创建(无状态组件,函数式组件)

// 函数创建组件 无状态组件 props
function Clock(props) {
  return (
    <div>
      <h1>现在是 {props.date.toLocaleTimeString()}.</h1>
    </div>
  );
}

ReactDOM.render(<Clock date={new Date()} />, document.getElementById("app"));
1
2
3
4
5
6
7
8
9
10

# 特点:

  • 无状态组件创建形式使代码的可读性更好,并减少了冗余代码
  • 组件不会被实例化,整体渲染性能得到提升
  • 组件不能访问 this 对象
  • 组件无法访问生命周期的方法(当然如果在使用无状态组件时发现需要用到生命周期时可以使用高阶组件)
  • 无状态组件只能访问输入的 props,同样的 props 会得到同样的渲染结果,不会有副作用

# 通过 Class 创建(有状态组件,类组件)

// class创建组件 有状态组件 继承React.Component this.props
class ES6Component extends React.Component {
  constructor(props) {
    super(props);

    // 设置 initial state
    this.state = {
      text: props.initialValue || "placeholder"
    };

    // ES6 类中函数必须手动绑定
    this.handleChange = this.handleChange.bind(this);
  }

  handleChange(event) {
    this.setState({
      text: event.target.value
    });
  }

  render() {
    return (
      <div>
        Type something:
        <input onChange={this.handleChange} value={this.state.text} />
      </div>
    );
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

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

  • 优先使用函数组件:适用于大多数场景,尤其是新项目。
  • Class 组件的剩余用途:
    • 错误边界(componentDidCatch)。
    • 需要 getSnapshotBeforeUpdate。
    • 依赖组件实例的第三方库集成。
  • 迁移策略:逐步替换 Class 组件,利用 Hooks 简化逻辑。

# 3.条件渲染

# &&运算符

render() {
    return (
        {this.state.alertStatus && <div>1111</div>}
    )
}
1
2
3
4
5

# 三元运算符

render() {
    return (
        {this.state.alertStatus ? <div>2222</div> : ''}
    )
}
1
2
3
4
5

# 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.Props(属性)和 State (状态)

React组件的prop属性为外部传入属性,不可修改

state为自身状态,用户交互通过改变状态实现重新渲染

# 5.1 React 中 禁止直接操作 state

// 应该通过this.setState 方法
this.setState({
    inputValue: e.target.value
})
1
2
3
4
// 在调用完setState之后,如果想拿到最新的state的值,可以会回调函数中拿
this.setState({
    
},function() { // 回调函数
    
})
1
2
3
4
5
6

# 5.2 state(状态)更新可能是异步的

React 为了优化性能,有可能会将多个 setState() 调用合并为一次更新。

因为 this.props 和 this.state 可能是异步更新的,你不能依赖他们的值计算下一个state(状态)

// 错误
this.setState({
  counter: this.state.counter + this.props.increment,
})
1
2
3
4

要弥补这个问题,使用另一种 setState() 的形式,它接受一个函数而不是一个对象。

这个函数将接收前一个状态作为第一个参数,应用更新时的 props 作为第二个参数

// 正确
this.setState((prevState, props) => ({
  counter: prevState.counter + props.increment
}))
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: 5/30/2025, 6:02:52 PM