首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >React 全家桶深度实战:从 Hooks 到状态路由一体化

React 全家桶深度实战:从 Hooks 到状态路由一体化

原创
作者头像
IT大佬 jzit-top
发布2026-08-17 14:30:27
发布2026-08-17 14:30:27
1160
举报

React 早已不只是一个 UI 库,围绕它构建的“全家桶”(React + React Router + Redux/ Zustand + 常用 Hooks 组合)已成为现代前端工程化的标准范式。本文不重复基础语法,而是从工程实践角度,带你实现一个具备认证、列表展示、全局状态和路由守卫的迷你应用,完整呈现全家桶的协同用法。


1. 项目初始化与依赖选型

使用 Vite 快速启动 TypeScript 项目:

bash

代码语言:javascript
复制
npm create vite@latest react-family -- --template react-ts
cd react-family
npm install react-router-dom zustand axios @tanstack/react-query
npm install -D @types/react @types/react-dom

我们选择:

  • 路由react-router-dom v6
  • 状态管理zustand(轻量,替代 Redux)
  • 数据请求@tanstack/react-query(缓存 + 异步状态管理)
  • HTTPaxios

2. 全局状态(Zustand)—— 用户认证模块

创建 store/authStore.ts

代码语言:javascript
复制
import { create } from 'zustand';
import { persist } from 'zustand/middleware';

interface AuthState {
  token: string | null;
  user: { id: string; name: string } | null;
  isAuthenticated: boolean;
  login: (token: string, user: { id: string; name: string }) => void;
  logout: () => void;
}

export const useAuthStore = create<AuthState>()(
  persist(
    (set) => ({
      token: null,
      user: null,
      isAuthenticated: false,
      login: (token, user) => set({ token, user, isAuthenticated: true }),
      logout: () => set({ token: null, user: null, isAuthenticated: false }),
    }),
    { name: 'auth-storage' } // 持久化到 localStorage
  )
);

使用 persist 中间件自动同步到本地存储,刷新页面不丢失登录态。


3. 路由配置与守卫(React Router v6)

创建 router/index.tsx

代码语言:javascript
复制
import { createBrowserRouter, Navigate } from 'react-router-dom';
import { lazy, Suspense } from 'react';
import { useAuthStore } from '@/store/authStore';

// 懒加载页面
const Login = lazy(() => import('@/pages/Login'));
const Dashboard = lazy(() => import('@/pages/Dashboard'));
const Profile = lazy(() => import('@/pages/Profile'));

// 路由守卫组件(高阶组件)
const PrivateRoute = ({ children }: { children: JSX.Element }) => {
  const isAuthenticated = useAuthStore((state) => state.isAuthenticated);
  return isAuthenticated ? children : <Navigate to="/login" replace />;
};

export const router = createBrowserRouter([
  {
    path: '/login',
    element: (
      <Suspense fallback={<div>Loading...</div>}>
        <Login />
      </Suspense>
    ),
  },
  {
    path: '/',
    element: (
      <PrivateRoute>
        <Suspense fallback={<div>Loading...</div>}>
          <Dashboard />
        </Suspense>
      </PrivateRoute>
    ),
    children: [
      { path: 'profile', element: <Profile /> },
    ],
  },
  { path: '*', element: <Navigate to="/" /> },
]);

注意 PrivateRoute 通过 Zustand 的 isAuthenticated 决定是否重定向,完全响应式。


4. 数据请求层(React Query + Axios)

创建 api/client.ts

代码语言:javascript
复制
import axios from 'axios';
import { useAuthStore } from '@/store/authStore';

export const apiClient = axios.create({
  baseURL: import.meta.env.VITE_API_BASE,
  timeout: 10000,
});

// 请求拦截器自动注入 token
apiClient.interceptors.request.use((config) => {
  const token = useAuthStore.getState().token;
  if (token) {
    config.headers.Authorization = `Bearer ${token}`;
  }
  return config;
});

// 响应拦截器:401 自动登出
apiClient.interceptors.response.use(
  (res) => res,
  (error) => {
    if (error.response?.status === 401) {
      useAuthStore.getState().logout();
      window.location.href = '/login';
    }
    return Promise.reject(error);
  }
);

定义 api/posts.ts

代码语言:javascript
复制
import { apiClient } from './client';

export interface Post {
  id: string;
  title: string;
  content: string;
}

export const fetchPosts = async (): Promise<Post[]> => {
  const { data } = await apiClient.get('/posts');
  return data;
};

在组件中使用 useQuery 获取数据:

代码语言:javascript
复制
// pages/Dashboard.tsx
import { useQuery } from '@tanstack/react-query';
import { fetchPosts } from '@/api/posts';

export default function Dashboard() {
  const { data, isLoading, error } = useQuery({
    queryKey: ['posts'],
    queryFn: fetchPosts,
    staleTime: 5 * 60 * 1000, // 5分钟新鲜
  });

  if (isLoading) return <div>Loading posts...</div>;
  if (error) return <div>Error: {error.message}</div>;

  return (
    <ul>
      {data?.map((post) => (
        <li key={post.id}>
          <h3>{post.title}</h3>
          <p>{post.content}</p>
        </li>
      ))}
    </ul>
  );
}

React Query 自动处理缓存、重试、后台刷新,替代了传统的 useEffect + useState 模式。


5. 自定义 Hooks —— 抽取复用逻辑

创建 hooks/useAuth.ts

代码语言:javascript
复制
import { useAuthStore } from '@/store/authStore';
import { useNavigate } from 'react-router-dom';
import { apiClient } from '@/api/client';

export const useAuth = () => {
  const { login, logout, isAuthenticated, user } = useAuthStore();
  const navigate = useNavigate();

  const signIn = async (email: string, password: string) => {
    const { data } = await apiClient.post('/auth/login', { email, password });
    login(data.token, data.user);
    navigate('/');
  };

  const signOut = () => {
    logout();
    navigate('/login');
  };

  return { signIn, signOut, isAuthenticated, user };
};

这个 Hook 封装了登录/登出的业务逻辑,并集成了 Zustand 和路由导航,组件只需调用 signIn 即可。


6. 登录页面完整实现

代码语言:javascript
复制
// pages/Login.tsx
import { useState } from 'react';
import { useAuth } from '@/hooks/useAuth';

export default function Login() {
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');
  const { signIn } = useAuth();
  const [loading, setLoading] = useState(false);

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault();
    setLoading(true);
    try {
      await signIn(email, password);
    } catch (err) {
      alert('Login failed');
    } finally {
      setLoading(false);
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <input type="email" value={email} onChange={(e) => setEmail(e.target.value)} />
      <input type="password" value={password} onChange={(e) => setPassword(e.target.value)} />
      <button type="submit" disabled={loading}>
        {loading ? 'Logging in...' : 'Login'}
      </button>
    </form>
  );
}

7. 性能优化与最佳实践

  • 路由懒加载:每个页面单独打包,减少首屏体积。
  • 状态原子化:Zustand 支持选择器,避免无关状态渲染。例如 const token = useAuthStore(state => state.token) 只会订阅 token 变化。
  • React Query 缓存策略:设置 staleTimecacheTime,避免重复请求。
  • 错误边界:使用 react-error-boundary 包裹路由,捕获渲染错误。

8. 总结

React 全家桶并非简单堆砌库,而是通过分层协作构建稳健的应用架构:

  • Zustand 管理全局可变状态(用户、主题、配置)
  • React Router 控制页面导航与权限
  • React Query 管理服务端数据,把异步复杂度交给库
  • 自定义 Hooks 抽离业务逻辑,保持组件纯净

以上代码可直接运行(需配合后端 API),完整体现了现代 React 工程化的核心范式。在实际项目中,你还可以加入 Zod 进行运行时校验、i18next 国际化、Vite 环境变量管理不同环境。全家桶的意义在于“组合”,而组合的关键在于清晰的边界设计——让每个工具做它最擅长的事,你的应用自然变得可维护、可扩展。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

目录
  • 1. 项目初始化与依赖选型
  • 2. 全局状态(Zustand)—— 用户认证模块
  • 3. 路由配置与守卫(React Router v6)
  • 4. 数据请求层(React Query + Axios)
  • 5. 自定义 Hooks —— 抽取复用逻辑
  • 6. 登录页面完整实现
  • 7. 性能优化与最佳实践
  • 8. 总结
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档