当前位置: 首页 > news >正文

React 高级教程

使用 React 高级组件(HOC)实现的完整项目示例,包含权限控制、数据加载状态处理、性能优化等常见高级功能。创建一个简单的博客系统:

// 项目结构:
src/
|-- components/
|   |-- ArticleList.jsx
|   |-- Article.jsx
|   |-- Header.jsx
|   |-- LoginForm.jsx
|   |-- UserProfile.jsx
|   |-- WithLoading.jsx
|   |-- AuthContext.jsx
|   |-- WithAuth.jsx
|-- hocs/
|   |-- withAuth.js
|-- hooks/
|   |-- useFetch.js
|   |-- useDebouncedFetch.js
|-- contexts/
|   |-- UserContext.js
|-- pages/
|   |-- HomePage.jsx
|   |-- AdminPage.jsx
|   |-- LoginPage.jsx
|   |-- UserProfilePage.jsx
|   |-- ArticleDetailPage.jsx
|-- App.jsx
|-- index.js

// 首先安装必要依赖:react-router-dom

关键技术点:

1. 创建认证上下文 (AuthContext.jsx)

import { createContext, useContext, useState } from 'react';

const AuthContext = createContext();

export function AuthProvider({ children }) {
  const [user, setUser] = useState(null);

  const login = (userData) => {
    setUser({ ...userData, role: 'admin' }); // 模拟登录
  };

  const logout = () => {
    setUser(null);
  };

  return (
    <AuthContext.Provider value={{ user, login, logout }}>
      {children}
    </AuthContext.Provider>
  );
}

export const useAuth = () => useContext(AuthContext);

2. 创建高阶组件 (WithAuth.jsx)

import { useNavigate } from 'react-router-dom';
import { useAuth } from '../contexts/AuthContext';

export const withAuth = (WrappedComponent, requiredRole = 'user') => {
  return (props) => {
    const { user } = useAuth();
    const navigate = useNavigate();

    if (!user) {
      navigate('/login');
      return null;
    }

    if (requiredRole === 'admin' && user.role !== 'admin') {
      return <div>无权限访问此页面</div>;
    }

    return <WrappedComponent {...props} user={user} />;
  };
};

3. 加载状态高阶组件 (WithLoading.jsx)

import { useState, useEffect } from 'react';

export const withLoading = (WrappedComponent, fetchData) => {
  return (props) => {
    const [data, setData] = useState(null);
    const [loading, setLoading] = useState(true);
    const [error, setError] = useState(null);

    useEffect(() => {
      const loadData = async () => {
        try {
          const result = await fetchData();
          setData(result);
        } catch (err) {
          setError(err.message);
        } finally {
          setLoading(false);
        }
      };

      loadData();
    }, []);

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

    return <WrappedComponent {...props} data={data} />;
  };
};

4. 自定义 Hook (useFetch.js)

import { useState, useEffect } from 'react';

export const useFetch = (url) => {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    const fetchData = async () => {
      try {
        const response = await fetch(url);
        const result = await response.json();
        setData(result);
      } catch (err) {
        setError(err.message);
      } finally {
        setLoading(false);
      }
    };

    fetchData();
  }, [url]);

  return { data, loading, error };
};

5. 优化列表组件 (OptimizedList.jsx)

import React from 'react';

const OptimizedList = React.memo(({ items, renderItem }) => {
  console.log('List re-rendered');
  return (
    <div style={{ maxHeight: '500px', overflow: 'auto' }}>
      {items.map((item, index) => (
        <div key={item.id || index}>
          {renderItem(item)}
        </div>
      ))}
    </div>
  );
});

export default OptimizedList;

6. 页面组件示例 (Admin.jsx)

import { withAuth } from '../components/WithAuth';
import { useFetch } from '../hooks/useFetch';
import OptimizedList from '../components/OptimizedList';

const AdminPanel = ({ user }) => {
  const { data: posts, loading, error } = useFetch('/api/posts');

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

  return (
    <div>
      <h1>欢迎管理员 {user.name}</h1>
      <h2>文章管理</h2>
      <OptimizedList
        items={posts}
        renderItem={(post) => (
          <div style={{ padding: '10px', borderBottom: '1px solid #ccc' }}>
            <h3>{post.title}</h3>
            <p>{post.content}</p>
          </div>
        )}
      />
    </div>
  );
};

// 使用高阶组件包裹,要求管理员权限
export default withAuth(AdminPanel, 'admin');

7. 主应用组件 (App.jsx)

// App.jsx
import React from 'react';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import { UserProvider } from './contexts/UserContext';
import HomePage from './pages/HomePage';
import AdminPage from './pages/AdminPage';
import LoginPage from './pages/LoginPage';
import UserProfilePage from './pages/UserProfilePage';
import ArticleDetailPage from './pages/ArticleDetailPage';

const App = () => {
  return (
    <UserProvider>
      <Router>
        <Switch>
          <Route path="/" exact component={HomePage} />
          <Route path="/login" component={LoginPage} />
          <Route path="/profile" component={UserProfilePage} />
          <Route path="/admin" component={AdminPage} />
          <Route path="/articles/:id" component={ArticleDetailPage} />
        </Switch>
      </Router>
    </UserProvider>
  );
};

export default App;

8. 登录页面示例 (Login.jsx)

import React, { useState, useContext } from 'react';
import { UserContext } from '../contexts/UserContext';

const LoginPage = () => {
  const { login } = useContext(UserContext);
  const [username, setUsername] = useState('');
  const [password, setPassword] = useState('');

  const handleSubmit = (e) => {
    e.preventDefault();
    // 假设我们直接使用固定的用户名和密码登录
    if (username === 'admin' && password === 'admin123') {
      login({ username, role: 'admin' });
    } else {
      alert('Invalid credentials');
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <input 
        type="text" 
        value={username} 
        onChange={(e) => setUsername(e.target.value)} 
        placeholder="Username" 
      />
      <input 
        type="password" 
        value={password} 
        onChange={(e) => setPassword(e.target.value)} 
        placeholder="Password" 
      />
      <button type="submit">Login</button>
    </form>
  );
};

export default LoginPage;

9. 文章详细页 (ArticleDetailPage.jsx)

展示文章的详细信息,点击文章标题进入。

// pages/ArticleDetailPage.jsx
import React, { useEffect, useState } from 'react';
import { useParams } from 'react-router-dom';
import { useFetch } from '../hooks/useFetch';

const ArticleDetailPage = () => {
  const { id } = useParams();
  const { data: article, loading, error } = useFetch(`/api/articles/${id}`);

  if (loading) return <p>Loading...</p>;
  if (error) return <p>Error: {error.message}</p>;

  return (
    <div>
      <h1>{article.title}</h1>
      <p>{article.content}</p>
    </div>
  );
};

export default ArticleDetailPage;

10. 用户个人资料页 (UserProfilePage.jsx)

用户可以更新个人资料。

// pages/UserProfilePage.jsx
import React, { useState, useContext } from 'react';
import { UserContext } from '../contexts/UserContext';

const UserProfilePage = () => {
  const { user, logout } = useContext(UserContext);
  const [username, setUsername] = useState(user.username);

  const handleSave = () => {
    // 在这里可以将更新后的用户名保存到后端
    console.log('Username updated:', username);
  };

  return (
    <div>
      <h1>User Profile</h1>
      <input 
        type="text" 
        value={username} 
        onChange={(e) => setUsername(e.target.value)} 
      />
      <button onClick={handleSave}>Save</button>
      <button onClick={logout}>Logout</button>
    </div>
  );
};

export default UserProfilePage;

11. 分页功能 (HomePage.jsx)

文章列表实现分页功能,每页显示一定数量的文章。

// pages/HomePage.jsx
import React, { useState } from 'react';
import { useFetch } from '../hooks/useFetch';
import { ArticleList } from '../components/ArticleList';

const HomePage = () => {
  const [page, setPage] = useState(1);
  const { data: articles, loading, error } = useFetch(`/api/articles?page=${page}`);

  if (loading) return <p>Loading...</p>;
  if (error) return <p>Error: {error.message}</p>;

  return (
    <div>
      <ArticleList articles={articles} />
      <button onClick={() => setPage(page - 1)} disabled={page === 1}>Previous</button>
      <button onClick={() => setPage(page + 1)}>Next</button>
    </div>
  );
};

export default HomePage;

11. 防抖(useDebouncedFetch.js)

用于处理防抖操作,避免频繁请求。

// hooks/useDebouncedFetch.js
import { useState, useEffect } from 'react';

export const useDebouncedFetch = (url, delay) => {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);
  
  useEffect(() => {
    const timer = setTimeout(() => {
      const fetchData = async () => {
        try {
          const response = await fetch(url);
          const result = await response.json();
          setData(result);
        } catch (error) {
          setError(error);
        } finally {
          setLoading(false);
        }
      };

      fetchData();
    }, delay);

    return () => clearTimeout(timer);
  }, [url, delay]);

  return { data, loading, error };
};

相关文章:

  • 【ArcGIS Pro二次开发】(87):样式_Style的用法
  • 【Spring AI】基于SpringAI+Vue3+ElementPlus的QA系统实现(前端)
  • flutter ListView Item复用源码解析
  • MySQL Workbench工具 导出导入数据库
  • spring学习(spring-DI(setter注入、构造器注入、自动装配方式))
  • 在 CentOS 系统中配置交换空间(Swap)解决内存不足
  • Android和DLT日志系统
  • 13.推荐系统的性能优化
  • Go语言协程Goroutine高级用法(一)
  • 分布式版本控制系统---git
  • 【openresty服务器】:源码编译openresty支持ssl,增加service系统服务,开机启动,自己本地签名证书,配置https访问
  • 基于巨控GRM552YW-CHE:西门子S7-1200 PLC远程程序上下载与实时调试方案
  • spring cloud 使用 webSocket
  • 怎麼使用靜態住宅IP進行多社媒帳號管理
  • A4988一款带转换器和过流保护的 DMOS 微步驱动器的使用方式
  • 探索高通骁龙游戏超分辨率技术:移动游戏的未来
  • 20240911 光迅科技 笔试
  • ProxySQL构建PolarDB-X标准版高可用路由服务三节点集群
  • 理解WebGPU 中的 GPUDevice :与 GPU 交互的核心接口
  • 【时时三省】(C语言基础)简单的算法举例
  • 专家分析丨乌美签署矿产协议,展现美外交困境下的无奈
  • 乌美签署矿产协议
  • 国务院任免国家工作人员:颜清辉任人社部副部长
  • 白玉兰奖征片综述丨国产剧集创作的此消彼长
  • 国际锐评:菲律宾“狐假虎威”把戏害的是谁?
  • 【社论】人工智能,年轻的事业