React核心组件 及 钩子函数应用

文章目录

一、React核心组件:函数式组件 vs 类组件

1. 类组件(Class Component)

概念

基于ES6类语法实现的组件,是React早期的主流写法,通过继承React.Component实现,内部通过this.state管理状态,this.props接收外部传参,生命周期方法控制组件渲染时机。

核心特点
  • 有完整的生命周期钩子(如componentDidMountcomponentDidUpdate
  • 状态(state)是不可直接修改的对象,需通过this.setState()更新
  • 需绑定this指向(常见问题:事件处理函数this丢失)
实战实例:类组件实现计数器
jsx 复制代码
import React from 'react';

// 类组件定义
class ClassCounter extends React.Component {
  // 1. 初始化状态
  constructor(props) {
    super(props); // 必须调用super接收props
    this.state = {
      count: 0, // 计数器初始值
      title: props.title || '类组件计数器' // 接收外部传参
    };
    // 绑定this(解决事件函数this丢失问题)
    this.handleIncrement = this.handleIncrement.bind(this);
  }

  // 2. 事件处理函数
  handleIncrement() {
    // 正确更新状态:setState是异步的,支持函数式更新(依赖旧状态)
    this.setState(prevState => ({
      count: prevState.count + 1
    }));
  }

  // 3. 生命周期:组件挂载完成后执行(仅执行一次)
  componentDidMount() {
    console.log('类组件挂载完成');
    // 模拟异步更新:3秒后自动加1
    this.timer = setTimeout(() => {
      this.setState({ count: this.state.count + 1 });
    }, 3000);
  }

  // 4. 生命周期:组件卸载前执行(清理副作用)
  componentWillUnmount() {
    clearTimeout(this.timer); // 清除定时器,防止内存泄漏
    console.log('类组件卸载');
  }

  // 5. 渲染函数(必须实现)
  render() {
    const { count, title } = this.state;
    return (
      <div style={{ padding: '20px', border: '1px solid #ccc' }}>
        <h3>{title}</h3>
        <p>当前计数:{count}</p>
        <button onClick={this.handleIncrement}>点击加1</button>
      </div>
    );
  }
}

// 使用组件
function App() {
  return <ClassCounter title="我的第一个类组件计数器" />;
}

export default App;
代码解释
  • constructor:初始化状态和绑定this,是类组件的构造方法;
  • setState:异步更新状态,函数式更新能避免"状态更新依赖旧值"的坑;
  • 生命周期:componentDidMount用于初始化异步操作(如请求数据、定时器),componentWillUnmount用于清理副作用(如定时器、事件监听)。

2. 函数式组件(Functional Component)

概念

基于普通JavaScript函数实现的组件,React 16.8引入Hooks后成为主流写法,无需继承、无this指向问题,通过Hooks(如useStateuseEffect)管理状态和生命周期。

核心特点
  • 简洁:无类语法、无this绑定问题
  • 无生命周期方法:通过useEffect模拟所有生命周期行为
  • 状态管理:通过useStateuseReducer等Hooks实现
  • 纯函数特性:输入(props)相同则输出(UI)相同(无副作用时)
实战实例:函数式组件实现计数器(对比类组件)
jsx 复制代码
import React, { useState, useEffect } from 'react';

// 函数式组件定义(接收props参数)
function FunctionalCounter(props) {
  // 1. 状态管理:useState(初始值) → [状态值, 状态更新函数]
  const [count, setCount] = useState(0);
  const [title] = useState(props.title || '函数式组件计数器');

  // 2. 事件处理函数(无需绑定this)
  const handleIncrement = () => {
    // 状态更新:直接调用setCount,支持函数式更新
    setCount(prevCount => prevCount + 1);
  };

  // 3. 模拟生命周期:useEffect(副作用函数, 依赖数组)
  // 依赖数组为空 → 仅在组件挂载/卸载时执行(对应componentDidMount + componentWillUnmount)
  useEffect(() => {
    console.log('函数式组件挂载完成');
    // 模拟异步更新
    const timer = setTimeout(() => {
      setCount(prev => prev + 1);
    }, 3000);

    // 清理函数(对应componentWillUnmount)
    return () => {
      clearTimeout(timer);
      console.log('函数式组件卸载');
    };
  }, []); // 空依赖 → 仅执行一次

  // 4. 渲染UI
  return (
    <div style={{ padding: '20px', border: '1px solid #ccc', marginTop: '10px' }}>
      <h3>{title}</h3>
      <p>当前计数:{count}</p>
      <button onClick={handleIncrement}>点击加1</button>
    </div>
  );
}

// 使用组件
function App() {
  return <FunctionalCounter title="我的第一个函数式组件计数器" />;
}

export default App;
代码解释
  • useState:替代类组件的this.state,返回"状态值+更新函数",支持直接更新或函数式更新;
  • useEffect:替代类组件的生命周期,第一个参数是"副作用函数",第二个参数是依赖数组,数组为空时仅执行一次(挂载),返回的函数用于清理副作用(卸载);
  • 函数式组件无需处理this,代码量比类组件少约30%,是React官方推荐的写法。

二、React组件传值:基础业务 vs 复杂业务

1. 基础业务传值(小型应用/局部组件通信)

适用于父子/兄弟/跨2-3层组件 通信,核心是props、回调函数、Context。

场景1:父传子(props)

场景:父组件传递"商品信息"给子组件展示

jsx 复制代码
// 父组件
function ProductList() {
  const products = [
    { id: 1, name: 'React实战教程', price: 99 },
    { id: 2, name: 'Vue实战教程', price: 89 }
  ];

  return (
    <div>
      <h2>商品列表</h2>
      {/* 父传子:通过props传递数据 */}
      {products.map(product => (
        <ProductItem key={product.id} product={product} />
      ))}
    </div>
  );
}

// 子组件(接收props)
function ProductItem(props) {
  // 解构props,简化使用
  const { product } = props;
  return (
    <div style={{ padding: '10px', border: '1px solid #eee' }}>
      <p>商品名称:{product.name}</p>
      <p>商品价格:¥{product.price}</p>
    </div>
  );
}
场景2:子传父(回调函数)

场景:子组件点击"加入购物车",通知父组件更新购物车数量

jsx 复制代码
// 父组件
function ShoppingCart() {
  const [cartCount, setCartCount] = useState(0);

  // 定义回调函数(子组件调用)
  const handleAddToCart = () => {
    setCartCount(prev => prev + 1);
  };

  return (
    <div>
      <h2>购物车({cartCount})</h2>
      {/* 父传子:传递回调函数 */}
      <ProductItem onAddToCart={handleAddToCart} />
    </div>
  );
}

// 子组件
function ProductItem() {
  return (
    <div>
      <p>React实战教程 ¥99</p>
      {/* 子调用父的回调函数,实现子传父 */}
      <button onClick={() => props.onAddToCart()}>加入购物车</button>
    </div>
  );
}
场景3:跨层级传值(useContext)

场景:App根组件的"用户信息"需要传递给深层的"个人中心"组件(避免props层层透传)

jsx 复制代码
// 1. 创建Context(默认值可选)
const UserContext = React.createContext({ name: '游客', id: 0 });

// 根组件
function App() {
  const user = { name: '张三', id: 1001 };

  return (
    {/* 2. 提供Context值 */}
    <UserContext.Provider value={user}>
      <div>
        <Header /> {/* 层级1 */}
      </div>
    </UserContext.Provider>
  );
}

// 层级1组件
function Header() {
  return <UserCenter />; // 层级2
}

// 层级2组件(接收Context)
function UserCenter() {
  // 3. 消费Context值
  const user = useContext(UserContext);
  return (
    <div>
      <h3>个人中心</h3>
      <p>用户名:{user.name}</p>
      <p>用户ID:{user.id}</p>
    </div>
  );
}

2. 复杂业务传值(中大型应用/全局状态)

适用于跨页面/跨模块 的全局状态管理,推荐轻量方案Zustand(中小应用)和工业级方案Redux(大型应用)。

场景1:Zustand(轻量全局状态)

适用场景:中大型应用,需要简单、无样板代码的全局状态管理(如购物车、用户信息、主题配置)。

步骤1:安装Zustand
bash 复制代码
npm install zustand
步骤2:实战实例:全局购物车状态
jsx 复制代码
// store/cartStore.js(创建全局store)
import { create } from 'zustand';

// 创建购物车store
const useCartStore = create((set, get) => ({
  // 1. 状态
  cartItems: [], // 购物车商品列表
  // 2. 计算属性(派生状态)
  get totalPrice() {
    return get().cartItems.reduce((sum, item) => sum + item.price * item.quantity, 0);
  },
  // 3. 方法(修改状态)
  addItem: (product) => set((state) => {
    // 检查商品是否已存在
    const existingItem = state.cartItems.find(item => item.id === product.id);
    if (existingItem) {
      // 已存在:更新数量
      return {
        cartItems: state.cartItems.map(item => 
          item.id === product.id 
            ? { ...item, quantity: item.quantity + 1 } 
            : item
        )
      };
    } else {
      // 不存在:新增商品
      return {
        cartItems: [...state.cartItems, { ...product, quantity: 1 }]
      };
    }
  }),
  removeItem: (productId) => set((state) => ({
    cartItems: state.cartItems.filter(item => item.id !== productId)
  }))
}));

// 组件1:商品列表(添加商品到购物车)
function ProductList() {
  const addItem = useCartStore(state => state.addItem);
  const products = [{ id: 1, name: 'React教程', price: 99 }, { id: 2, name: 'Vue教程', price: 89 }];

  return (
    <div>
      <h3>商品列表</h3>
      {products.map(product => (
        <div key={product.id} style={{ margin: '10px 0' }}>
          <p>{product.name} ¥{product.price}</p>
          <button onClick={() => addItem(product)}>加入购物车</button>
        </div>
      ))}
    </div>
  );
}

// 组件2:购物车(展示/删除商品)
function ShoppingCart() {
  // 消费store的状态和方法
  const cartItems = useCartStore(state => state.cartItems);
  const totalPrice = useCartStore(state => state.totalPrice);
  const removeItem = useCartStore(state => state.removeItem);

  return (
    <div style={{ marginTop: '20px', border: '1px solid #ccc', padding: '10px' }}>
      <h3>购物车(总价:¥{totalPrice})</h3>
      {cartItems.length === 0 ? (
        <p>购物车为空</p>
      ) : (
        cartItems.map(item => (
          <div key={item.id}>
            <p>{item.name} × {item.quantity} ¥{item.price * item.quantity}</p>
            <button onClick={() => removeItem(item.id)}>删除</button>
          </div>
        ))
      )}
    </div>
  );
}

// 根组件
function App() {
  return (
    <div>
      <ProductList />
      <ShoppingCart />
    </div>
  );
}
场景2:Redux(大型应用全局状态)

适用场景:超大型应用(如电商、后台系统),需要严格的状态管理规范、中间件支持(如异步请求、日志)。

步骤1:安装依赖
bash 复制代码
npm install @reduxjs/toolkit react-redux
步骤2:实战实例:全局用户状态
jsx 复制代码
// store/userSlice.js(创建Slice)
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';

// 异步action:模拟请求用户信息
export const fetchUserInfo = createAsyncThunk(
  'user/fetchUserInfo',
  async (userId, { rejectWithValue }) => {
    try {
      // 模拟API请求
      const res = await new Promise((resolve) => {
        setTimeout(() => resolve({ id: userId, name: '张三', role: 'admin' }), 1000);
      });
      return res;
    } catch (err) {
      return rejectWithValue(err.message);
    }
  }
);

// 创建用户Slice
const userSlice = createSlice({
  name: 'user',
  initialState: {
    info: null, // 用户信息
    loading: false, // 加载状态
    error: null // 错误信息
  },
  reducers: {
    // 同步action:更新用户名
    updateUserName: (state, action) => {
      if (state.info) {
        state.info.name = action.payload;
      }
    }
  },
  // 处理异步action
  extraReducers: (builder) => {
    builder
      .addCase(fetchUserInfo.pending, (state) => {
        state.loading = true;
        state.error = null;
      })
      .addCase(fetchUserInfo.fulfilled, (state, action) => {
        state.loading = false;
        state.info = action.payload;
      })
      .addCase(fetchUserInfo.rejected, (state, action) => {
        state.loading = false;
        state.error = action.payload || '请求失败';
      });
  }
});

// 导出同步action
export const { updateUserName } = userSlice.actions;

// 导出reducer
export default userSlice.reducer;

// store/index.js(配置Store)
import { configureStore } from '@reduxjs/toolkit';
import { Provider } from 'react-redux';
import userReducer from './userSlice';

// 创建store
const store = configureStore({
  reducer: {
    user: userReducer // 注册user reducer
  }
});

// 根组件包裹Provider
function AppProvider({ children }) {
  return <Provider store={store}>{children}</Provider>;
}

// 组件1:用户信息展示(消费Redux状态)
function UserInfo() {
  // 从store获取状态
  const { info, loading, error } = useSelector((state) => state.user);
  // 获取dispatch方法
  const dispatch = useDispatch();

  // 触发异步请求
  useEffect(() => {
    dispatch(fetchUserInfo(1001));
  }, [dispatch]);

  // 触发同步更新
  const handleUpdateName = () => {
    dispatch(updateUserName('李四'));
  };

  if (loading) return <p>加载中...</p>;
  if (error) return <p>错误:{error}</p>;
  if (!info) return <p>未登录</p>;

  return (
    <div>
      <h3>用户信息</h3>
      <p>ID:{info.id}</p>
      <p>姓名:{info.name}</p>
      <p>角色:{info.role}</p>
      <button onClick={handleUpdateName}>修改姓名为李四</button>
    </div>
  );
}

// 最终App组件
function App() {
  return (
    <AppProvider>
      <UserInfo />
    </AppProvider>
  );
}

三、React最新钩子函数(React 18+)全场景应用

1. 基础钩子(必学)

钩子 核心作用 典型场景 实例代码
useState 管理组件状态 计数器、表单输入、开关状态 const [count, setCount] = useState(0);
useEffect 处理副作用(异步/生命周期) 数据请求、定时器、事件监听 见"函数式计数器"的useEffect实例
useRef 保存可变值/获取DOM元素 避免重复请求、获取输入框DOM const inputRef = useRef(null); <input ref={inputRef} />
useContext 跨层级获取Context值 全局主题、用户信息 见"跨层级传值"实例

2. 性能优化钩子

useMemo(缓存计算结果)

场景:避免复杂计算(如列表过滤、数据格式化)在每次渲染时重复执行

jsx 复制代码
function ProductFilter({ products, keyword }) {
  // 缓存过滤结果:仅当products或keyword变化时重新计算
  const filteredProducts = useMemo(() => {
    console.log('执行过滤计算'); // 仅依赖变化时打印
    return products.filter(product => 
      product.name.toLowerCase().includes(keyword.toLowerCase())
    );
  }, [products, keyword]); // 依赖数组

  return (
    <div>
      {filteredProducts.map(product => (
        <div key={product.id}>{product.name}</div>
      ))}
    </div>
  );
}
useCallback(缓存函数引用)

场景:传递给子组件的回调函数,避免子组件不必要的重渲染

jsx 复制代码
// 父组件
function Parent() {
  const [count, setCount] = useState(0);

  // 缓存函数:仅当count变化时重新创建函数
  const handleClick = useCallback(() => {
    console.log('点击了', count);
  }, [count]);

  return <Child onClick={handleClick} />;
}

// 子组件(React.memo避免不必要重渲染)
const Child = React.memo(({ onClick }) => {
  console.log('子组件渲染'); // 仅handleClick变化时打印
  return <button onClick={onClick}>点击</button>;
});

3. React 18新增钩子(进阶)

useId(生成唯一ID)

场景:表单label与input关联、无障碍(a11y)属性、动态生成DOM的唯一标识

jsx 复制代码
function FormInput() {
  // 生成唯一ID
  const inputId = useId();

  return (
    <div>
      <label htmlFor={inputId}>用户名:</label>
      <input id={inputId} type="text" />
    </div>
  );
}
useTransition(非阻塞更新)

场景:耗时操作(如大数据列表筛选),避免页面卡顿(优先级低于用户交互)

jsx 复制代码
function BigList() {
  const [keyword, setKeyword] = useState('');
  const [list, setList] = useState([]);
  // 标记过渡状态:isPending为true时显示加载提示
  const [isPending, startTransition] = useTransition();

  // 监听关键词变化,触发低优先级更新
  useEffect(() => {
    // startTransition包裹耗时操作,不阻塞UI
    startTransition(() => {
      // 模拟大数据筛选(10万条数据)
      const filteredList = Array.from({ length: 100000 })
        .map((_, i) => `Item ${i}`)
        .filter(item => item.includes(keyword));
      setList(filteredList);
    });
  }, [keyword]);

  return (
    <div>
      <input 
        type="text" 
        value={keyword} 
        onChange={(e) => setKeyword(e.target.value)} 
        placeholder="搜索..." 
      />
      {isPending && <p>加载中...</p>}
      <ul>
        {list.map((item, i) => <li key={i}>{item}</li>)}
      </ul>
    </div>
  );
}
useDeferredValue(延迟更新值)

场景 :与useTransition配合,延迟更新非关键UI(如搜索结果预览)

jsx 复制代码
function Search() {
  const [keyword, setKeyword] = useState('');
  // 延迟更新keyword:优先响应用户输入,再更新结果
  const deferredKeyword = useDeferredValue(keyword, { timeoutMs: 200 });

  // 仅当deferredKeyword变化时筛选数据
  const results = useMemo(() => {
    return Array.from({ length: 1000 })
      .map((_, i) => `Result ${i}: ${deferredKeyword}`);
  }, [deferredKeyword]);

  return (
    <div>
      <input 
        type="text" 
        value={keyword} 
        onChange={(e) => setKeyword(e.target.value)} 
        placeholder="输入搜索词..." 
      />
      <div>
        {results.map((item, i) => <div key={i}>{item}</div>)}
      </div>
    </div>
  );
}
useSyncExternalStore(同步外部状态)

场景:订阅外部数据源(如Redux、Zustand、浏览器API),兼容React并发模式

jsx 复制代码
// 订阅浏览器窗口大小变化
function WindowSize() {
  // 订阅外部状态(窗口大小)
  const size = useSyncExternalStore(
    // 1. 订阅函数:返回取消订阅的函数
    (callback) => {
      window.addEventListener('resize', callback);
      return () => window.removeEventListener('resize', callback);
    },
    // 2. 获取当前状态的函数
    () => ({ width: window.innerWidth, height: window.innerHeight }),
    // 3. 服务端渲染的默认值
    () => ({ width: 0, height: 0 })
  );

  return (
    <div>
      <p>窗口宽度:{size.width}px</p>
      <p>窗口高度:{size.height}px</p>
    </div>
  );
}

总结

  1. 组件选型 :函数式组件+Hooks是React主流写法,类组件仅需了解(维护老项目),函数式组件无this问题、代码更简洁;
  2. 传值方案 :基础传值用props/回调/Context(局部通信),复杂传值用Zustand(轻量)/Redux(大型应用),Zustand上手成本远低于Redux;
  3. 钩子应用 :基础钩子(useState/useEffect/useRef)是核心,性能优化用useMemo/useCallback,React 18新增钩子(useTransition/useDeferredValue)解决并发渲染下的卡顿问题。

通过以上实例和场景,你可以覆盖React从基础到实战的核心知识点,建议先掌握函数式组件和基础钩子,再逐步学习状态管理和进阶钩子,结合实际业务场景练习会更容易消化。

思考

React 18中的useTransitionuseDeferredValueuseSyncExternalStore这三个钩子怎么理解与宏任务、微任务是否关联。


核心前提:先分清「浏览器任务队列」和「React调度系统」

要回答这个问题,首先要纠正一个关键认知:这三个钩子本身既不是宏任务,也不是微任务

浏览器的宏/微任务是JS引擎层面的任务队列机制:

类型 包含内容 执行优先级
宏任务 script、setTimeout、setInterval、MessageChannel、UI渲染等
微任务 Promise.then、queueMicrotask、MutationObserver等 高(宏任务执行完立即执行)

useTransition等钩子是React 18并发渲染 特性的核心API,依赖的是React自身的「Scheduler(调度器)」系统------它基于浏览器的宏任务(主要是MessageChannel)实现,但本质是React对任务优先级的调度,而非直接对应浏览器的宏/微任务分类。

一、逐个解析:钩子的本质与实现原理

1. useTransition:标记「低优先级更新」

核心定位

不是任务类型,而是React内部的更新优先级标记,用于将「非紧急更新」(如大数据列表筛选、复杂计算)标记为「过渡更新(Transition)」,让「紧急更新」(如用户输入、点击)优先执行,避免页面卡顿。

实现原理
  1. 优先级划分 :React将更新分为两类:
    • 紧急更新(Urgent):用户交互(输入、点击、滚动),必须同步执行,阻塞主线程;
    • 过渡更新(Transition):useTransition包裹的更新,低优先级,可中断、可延后。
  2. 底层调度
    • React Scheduler(调度器)使用MessageChannel创建宏任务(放弃了requestIdleCallback,因为触发时机不可控),为不同优先级的任务分配「过期时间(expiration time)」------过期时间越短,优先级越高。
    • 当浏览器主线程空闲时,Scheduler会取出高优先级任务执行;如果低优先级任务执行中来了高优先级任务,低优先级任务会被中断,等高优先级任务执行完再恢复。
  3. 关键效果isPending状态会标识过渡更新是否在进行中,让你可以展示加载提示,且用户交互不会被阻塞。
场景实例(补充原理说明)
jsx 复制代码
import { useState, useTransition } from 'react';

function BigList() {
  const [keyword, setKeyword] = useState('');
  const [list, setList] = useState([]);
  const [isPending, startTransition] = useTransition();

  useEffect(() => {
    // 1. 这里的更新被标记为低优先级(Transition)
    startTransition(() => {
      // 模拟10万条数据筛选(耗时操作)
      const filtered = Array.from({ length: 100000 })
        .map((_, i) => `Item ${i}`)
        .filter(item => item.includes(keyword));
      // 2. 这个setList是低优先级更新,可被用户输入中断
      setList(filtered);
    });
  }, [keyword]);

  return (
    <div>
      {/* 3. 输入框的setKeyword是紧急更新,优先执行 */}
      <input 
        value={keyword} 
        onChange={(e) => setKeyword(e.target.value)} 
        placeholder="搜索..." 
      />
      {isPending && <p>加载中...</p>}
      <ul>{list.map((item, i) => <li key={i}>{item}</li>)}</ul>
    </div>
  );
}

2. useDeferredValue:延迟更新「非关键值」

核心定位

useDeferredValueuseTransition的「值版本」,本质是让一个状态的更新滞后于紧急更新,而非直接关联宏/微任务。

实现原理
  1. 值的延迟同步 :当依赖的状态(如keyword)变化时,React会先更新「紧急值」(输入框的keyword),然后在浏览器空闲时,异步更新deferredValue(延迟值)。
  2. 优先级复用 :底层复用React Scheduler的优先级调度机制,deferredValue的更新优先级与useTransition的过渡更新一致,属于低优先级。
  3. 缓存与重渲染 :配合useMemo使用时,可避免低优先级值变化导致的重复计算,进一步优化性能。
场景实例(补充原理说明)
jsx 复制代码
import { useState, useDeferredValue, useMemo } from 'react';

function Search() {
  const [keyword, setKeyword] = useState('');
  // 1. deferredKeyword 会滞后于 keyword 更新(低优先级)
  const deferredKeyword = useDeferredValue(keyword, { timeoutMs: 200 });

  // 2. 仅当 deferredKeyword 变化时才重新筛选(避免输入时频繁计算)
  const results = useMemo(() => {
    return Array.from({ length: 1000 })
      .map((_, i) => `Result ${i}: ${deferredKeyword}`);
  }, [deferredKeyword]);

  return (
    <div>
      {/* 3. 输入框的更新是紧急的,优先响应 */}
      <input 
        value={keyword} 
        onChange={(e) => setKeyword(e.target.value)} 
      />
      {/* 4. 结果列表基于延迟值渲染,不阻塞输入 */}
      <div>{results.map((item, i) => <div key={i}>{item}</div>)}</div>
    </div>
  );
}

3. useSyncExternalStore:同步外部状态到React

核心定位

不是任务调度钩子,而是适配并发渲染的外部状态订阅工具,确保React组件能同步外部数据源(如Redux、Zustand、浏览器API)的状态,避免并发渲染下的数据不一致。

实现原理
  1. 订阅-取消订阅封装
    • 接收三个参数:subscribe(订阅外部状态)、getSnapshot(获取当前状态)、getServerSnapshot(服务端渲染默认值)。
    • 当外部状态变化时,subscribe中的回调会触发React组件更新,这个更新遵循React的优先级规则。
  2. 并发渲染适配
    • 在React并发渲染(如更新被中断)时,useSyncExternalStore会确保组件最终拿到的是最新的外部状态,而非中断前的旧状态。
    • 它不直接操作宏/微任务,而是封装了订阅逻辑,让外部状态的更新能兼容React的调度机制。
场景实例(补充原理说明)
jsx 复制代码
import { useSyncExternalStore } from 'react';

// 外部数据源:浏览器窗口大小(非React状态)
function subscribeToWindowSize(callback) {
  window.addEventListener('resize', callback);
  // 返回取消订阅函数(适配组件卸载)
  return () => window.removeEventListener('resize', callback);
}

function getWindowSizeSnapshot() {
  return { width: window.innerWidth, height: window.innerHeight };
}

function WindowSize() {
  // 1. 同步外部状态到组件,兼容并发渲染
  const size = useSyncExternalStore(
    subscribeToWindowSize, // 订阅
    getWindowSizeSnapshot, // 获取当前状态
    () => ({ width: 0, height: 0 }) // 服务端默认值
  );

  return (
    <div>
      <p>宽度:{size.width}px</p>
      <p>高度:{size.height}px</p>
    </div>
  );
}

二、关键补充:React Scheduler的底层实现

React的调度器(scheduler包)是实现上述钩子的核心,它的底层逻辑:

  1. 放弃requestIdleCallback:因为其触发时机不可控(只有浏览器空闲时才触发),且兼容性差。
  2. 使用MessageChannel创建宏任务
    • MessageChannel的port.postMessage()会创建一个宏任务,执行时机比setTimeout更精准(无4ms延迟)。
    • Scheduler将不同优先级的任务放入队列,每次执行完一个任务后,检查是否有更高优先级的任务,若有则中断当前任务,优先执行高优先级任务。
  3. 优先级与过期时间
    • 每个任务都有一个「过期时间」(如紧急任务过期时间是0,过渡任务过期时间是500ms)。
    • 过期时间越短,优先级越高;任务过期后,会被提升为紧急任务强制执行。

总结

  1. 核心结论useTransition/useDeferredValue/useSyncExternalStore既不是宏任务也不是微任务,它们是React基于自身Scheduler(调度器)实现的优先级调度/状态同步工具,底层依赖MessageChannel(宏任务)实现调度,但本身不属于浏览器任务队列分类。
  2. useTransition/useDeferredValue:核心是标记低优先级更新,让用户交互(紧急更新)不被耗时操作阻塞,提升页面响应性。
  3. useSyncExternalStore:核心是适配React并发渲染,确保外部状态与组件状态同步,避免数据不一致问题。
相关推荐
小宇的天下2 小时前
Calibre nmDRC-H 层级化 DRC
java·服务器·前端
怒放de生命20102 小时前
前端子包+docker流水线部署+nginx代理部署
前端·nginx·docker
唐钰小球2 小时前
layer.prompt文本框为空时也能提交的方法
javascript·prompt·layui
0思必得02 小时前
[Web自动化] Selenium浏览器对象属性
前端·python·selenium·自动化·web自动化
小二·2 小时前
Python Web 开发进阶实战:边缘智能网关 —— 在 Flask + Vue 中构建轻量级 IoT 边缘计算平台
前端·python·flask
是毛毛吧2 小时前
GitHub热榜----前端已死?AionUi 横空出世:首个开源“生成式UI”框架,让 AI 在运行时“手搓”界面
前端·开源·github
斯普信专业组2 小时前
ReAct Agent 解析:从理论到实践的高效推理框架
前端·react.js·前端框架
徐同保2 小时前
开发onlyoffice插件,功能是选择文本后立即通知父页面
开发语言·前端·javascript
23124_802 小时前
Base64多层嵌套解码
前端·javascript·数据库