本文最后更新于 2024-10-17,文章内容可能已经过时。

React 进阶阶段学习计划

目标

  • 掌握自定义Hooks的创建和使用。
  • 深入理解上下文(Context)和Redux的高级用法。
  • 学会服务端渲染(SSR)。
  • 深入探讨性能优化技巧。

学习内容

自定义Hooks

创建和使用自定义Hooks

  • 自定义Hooks:用于提取组件逻辑,使代码更加模块化和复用。

  • 示例

    // useFetch.js
    import { useState, useEffect } from 'react';
    
    function useFetch(url) {
      const [data, setData] = useState(null);
      const [loading, setLoading] = useState(true);
      const [error, setError] = useState(null);
    
      useEffect(() => {
        async function fetchData() {
          try {
            const response = await fetch(url);
            const json = await response.json();
            setData(json);
          } catch (err) {
            setError(err);
          } finally {
            setLoading(false);
          }
        }
    
        fetchData();
      }, [url]);
    
      return { data, loading, error };
    }
    
    export default useFetch;
    
    // App.js
    import React from 'react';
    import useFetch from './useFetch';
    
    function App() {
      const { data, loading, error } = useFetch('https://jsonplaceholder.typicode.com/posts');
    
      if (loading) return <div>Loading...</div>;
      if (error) return <div>Error: {error.message}</div>;
    
      return (
        <div>
          <h1>Data from API</h1>
          <ul>
            {data.map(item => (
              <li key={item.id}>{item.title}</li>
            ))}
          </ul>
        </div>
      );
    }
    
    export default App;
    

上下文(Context)和Redux的高级用法

上下文(Context)的高级用法

  • Context Consumer:用于在函数组件中消费Context。
  • 示例
    // ThemeContext.js
    import React, { createContext, useContext, useState } from 'react';
    
    const ThemeContext = createContext();
    
    // 提供者组件
    function ThemeProvider({ children }) {
      const [theme, setTheme] = useState('light');
    
      const toggleTheme = () => {
        setTheme((prevTheme) => (prevTheme === 'light' ? 'dark' : 'light'));
      };
    
      return (
        <ThemeContext.Provider value={{ theme, toggleTheme }}>
          {children}
        </ThemeContext.Provider>
      );
    }
    
    // 消费者组件
    function ThemedButton() {
      const { theme, toggleTheme } = useContext(ThemeContext);
    
      return (
        <button onClick={toggleTheme} style={{ background: theme === 'light' ? '#fff' : '#333', color: theme === 'light' ? '#000' : '#fff' }}>
          Toggle Theme
        </button>
      );
    }
    
    // App.js
    import React from 'react';
    import ThemeProvider from './ThemeProvider';
    import ThemedButton from './ThemedButton';
    
    function App() {
      return (
        <ThemeProvider>
          <ThemedButton />
        </ThemeProvider>
      );
    }
    
    export default App;
    

Redux的高级用法

  • Redux Toolkit:简化Redux的开发流程。

  • 安装

    npm install @reduxjs/toolkit react-redux
    
  • 示例

    // store.js
    import { configureStore, createSlice } from '@reduxjs/toolkit';
    
    const counterSlice = createSlice({
      name: 'counter',
      initialState: { value: 0 },
      reducers: {
        increment: (state) => {
          state.value += 1;
        },
        decrement: (state) => {
          state.value -= 1;
        },
      },
    });
    
    const store = configureStore({
      reducer: {
        counter: counterSlice.reducer,
      },
    });
    
    export const { increment, decrement } = counterSlice.actions;
    export default store;
    
    // App.js
    import React from 'react';
    import { useSelector, useDispatch } from 'react-redux';
    import { increment, decrement } from './store';
    import { Provider } from 'react-redux';
    import store from './store';
    
    function Counter() {
      const count = useSelector((state) => state.counter.value);
      const dispatch = useDispatch();
    
      return (
        <div>
          <p>Count: {count}</p>
          <button onClick={() => dispatch(increment())}>Increment</button>
          <button onClick={() => dispatch(decrement())}>Decrement</button>
        </div>
      );
    }
    
    function App() {
      return (
        <Provider store={store}>
          <Counter />
        </Provider>
      );
    }
    
    export default App;
    

服务端渲染(SSR)

Next.js

  • Next.js:一个流行的React框架,支持服务端渲染。

  • 安装

    npx create-next-app@latest my-app
    cd my-app
    npm run dev
    
  • 示例

    // pages/index.js
    import { useState } from 'react';
    
    export default function Home() {
      const [count, setCount] = useState(0);
    
      return (
        <div>
          <h1>Welcome to Next.js!</h1>
          <p>Count: {count}</p>
          <button onClick={() => setCount(count + 1)}>Increment</button>
        </div>
      );
    }
    
    // pages/api/hello.js
    export default function handler(req, res) {
      res.status(200).json({ name: 'John Doe' });
    }
    

性能优化的深入探讨

使用React Profiler

  • React Profiler:用于分析组件的渲染性能。
  • 启用Profiler
    import React, { Profiler } from 'react';
    
    function onRender(id, phase, actualDuration, baseDuration, startTime, commitTime) {
      console.log({ id, phase, actualDuration, baseDuration, startTime, commitTime });
    }
    
    function App() {
      return (
        <Profiler id="App" onRender={onRender}>
          <div>
            <h1>React Profiler Example</h1>
            {/* 其他组件 */}
          </div>
        </Profiler>
      );
    }
    
    export default App;
    

使用React.memo和PureComponent

  • React.memo:用于优化函数组件的性能。

  • PureComponent:用于优化类组件的性能。

  • 示例

    // MyComponent.js
    import React, { PureComponent } from 'react';
    
    class MyComponent extends PureComponent {
      render() {
        return <div>{this.props.value}</div>;
      }
    }
    
    export default MyComponent;
    
    // MyFunctionComponent.js
    import React from 'react';
    
    const MyFunctionComponent = React.memo(({ value }) => {
      return <div>{value}</div>;
    });
    
    export default MyFunctionComponent;
    

实践项目

社交媒体应用

  1. 创建项目
    npx create-next-app@latest social-media-app
    cd social-media-app
    npm run dev
    
  2. 创建组件
    • PostList.js:显示帖子列表
      import React, { useState, useEffect } from 'react';
      import axios from 'axios';
      
      function PostList() {
        const [posts, setPosts] = useState([]);
      
        useEffect(() => {
          axios.get('https://jsonplaceholder.typicode.com/posts')
            .then(response => {
              setPosts(response.data);
            })
            .catch(error => {
              console.error('Error fetching posts:', error);
            });
        }, []);
      
        return (
          <div className="post-list">
            {posts.map(post => (
              <div key={post.id} className="post">
                <h3>{post.title}</h3>
                <p>{post.body}</p>
              </div>
            ))}
          </div>
        );
      }
      
      export default PostList;
      
    • App.js:主组件
      import React from 'react';
      import PostList from '../components/PostList';
      
      function Home() {
        return (
          <div className="container">
            <h1>Social Media App</h1>
            <PostList />
          </div>
        );
      }
      
      export default Home;
      

博客管理系统

  1. 创建项目
    npx create-react-app blog-management --template typescript
    cd blog-management
    npm start
    
  2. 安装axios
    npm install axios
    
  3. 创建组件
    • PostForm.tsx:添加和编辑帖子的表单

      import React, { useState } from 'react';
      import axios from 'axios';
      
      interface Post {
        id?: number;
        title: string;
        content: string;
      }
      
      interface PostFormProps {
        initialPost?: Post;
        onSubmit: (post: Post) => void;
      }
      
      const PostForm: React.FC<PostFormProps> = ({ initialPost, onSubmit }) => {
        const [post, setPost] = useState<Post>({
          id: initialPost?.id,
          title: initialPost?.title || '',
          content: initialPost?.content || '',
        });
      
        const handleSubmit = async (e: React.FormEvent) => {
          e.preventDefault();
          onSubmit(post);
          setPost({ title: '', content: '' });
        };
      
        return (
          <form onSubmit={handleSubmit}>
            <input
              type="text"
              value={post.title}
              onChange={(e) => setPost({ ...post, title: e.target.value })}
              placeholder="Title"
            />
            <textarea
              value={post.content}
              onChange={(e) => setPost({ ...post, content: e.target.value })}
              placeholder="Content"
            />
            <button type="submit">Submit</button>
          </form>
        );
      };
      
      export default PostForm;
      
    • PostList.tsx:显示帖子列表

      import React, { useState, useEffect } from 'react';
      import axios from 'axios';
      import PostForm from './PostForm';
      
      interface Post {
        id: number;
        title: string;
        content: string;
      }
      
      const PostList: React.FC = () => {
        const [posts, setPosts] = useState<Post[]>([]);
        const [editingPost, setEditingPost] = useState<Post | null>(null);
      
        useEffect(() => {
          axios.get<Post[]>('https://jsonplaceholder.typicode.com/posts')
            .then(response => {
              setPosts(response.data.slice(0, 10));
            })
            .catch(error => {
              console.error('Error fetching posts:', error);
            });
        }, []);
      
        const handleEdit = (post: Post) => {
          setEditingPost(post);
        };
      
        const handleDelete = (postId: number) => {
          setPosts(posts.filter(post => post.id !== postId));
        };
      
        const handleSave = (post: Post) => {
          if (post.id) {
            setPosts(posts.map(p => (p.id === post.id ? post : p)));
          } else {
            setPosts([...posts, post]);
          }
          setEditingPost(null);
        };
      
        return (
          <div className="post-list">
            {editingPost ? (
              <PostForm initialPost={editingPost} onSubmit={handleSave} />
            ) : (
              <PostForm onSubmit={handleSave} />
            )}
            <ul>
              {posts.map(post => (
                <li key={post.id}>
                  <h3>{post.title}</h3>
                  <p>{post.content}</p>
                  <button onClick={() => handleEdit(post)}>Edit</button>
                  <button onClick={() => handleDelete(post.id)}>Delete</button>
                </li>
              ))}
            </ul>
          </div>
        );
      };
      
      export default PostList;
      
    • App.tsx:主组件

      import React from 'react';
      import PostList from './components/PostList';
      
      const App: React.FC = () => {
        return (
          <div className="App">
            <h1>Blog Management System</h1>
            <PostList />
          </div>
        );
      };
      
      export default App;
      

建议

  • 定期回顾:每周花时间回顾本周所学内容,确保知识点牢固掌握。
  • 参与社区:加入React相关的论坛、Slack群组或Discord服务器,与其他开发者交流心得。
  • 阅读源码:尝试阅读一些复杂的React库的源码,提高代码理解和分析能力。

希望这个学习计划能够帮助你系统地学习React进阶技能,并通过实践项目巩固所学知识。祝你学习顺利!


你可以将上述Markdown内容复制到任何支持Markdown的编辑器或平台中,以便于查看和使用。