likes
comments
collection
share

React notes

作者站长头像
站长
· 阅读数 45

reducer action type definition

在 React 中使用 useReducer 时,可以使用 TypeScript 来标注 action 的类型。下面是一个示例:

import { useReducer } from 'react';

type State = {
  count: number;
};

type Action =
  | { type: 'increment'; payload: number }
  | { type: 'decrement'; payload: number };

const initialState: State = {
  count: 0,
};

const reducer = (state: State, action: Action): State => {
  switch (action.type) {
    case 'increment':
      return { count: state.count + action.payload };
    case 'decrement':
      return { count: state.count - action.payload };
    default:
      throw new Error('Unknown action type');
  }
};

const Counter = () => {
  const [state, dispatch] = useReducer(reducer, initialState);

  const handleIncrement = () => {
    dispatch({ type: 'increment', payload: 1 });
  };

  const handleDecrement = () => {
    dispatch({ type: 'decrement', payload: 1 });
  };

  return (
    <div>
      <button onClick={handleIncrement}>Increment</button>
      <button onClick={handleDecrement}>Decrement</button>
      <p>Count: {state.count}</p>
    </div>
  );
};

在上述示例中,我们定义了 Action 类型,它是一个联合类型,包含了所有可能的 action 类型。每个具体的 action 都需要包含 type 属性来指示其类型,以及其他可选的 payload 属性来携带额外的数据。

reducer 函数中,我们对 action 进行了类型标注,确保只处理已定义的 action 类型。如果出现未知的 action 类型,我们抛出一个错误。

在组件中,我们使用 useReducer 来创建状态 statedispatch 函数。我们定义了两个处理函数 handleIncrementhandleDecrement,并在调用 dispatch 时传递了相应的 action。

这样,在使用 useReducer 的过程中,你就可以使用 TypeScript 来明确标注 action 的类型,以提供更好的类型检查和类型推断功能。

useContext with state

在 React 中,可以使用状态(state)来管理 Context。一种常见的做法是使用 Context 提供器(Context Provider)组件,并在其内部使用状态来跟踪和更新提供的值。这样,通过 Context,可以在组件树中的其他组件中访问和更新该状态。

下面是一个示例,展示了如何在 Context 中使用状态:

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

// 创建一个 Context
const MyContext = createContext();

// 创建 Context 提供器组件
const MyContextProvider = ({ children }) => {
  const [value, setValue] = useState('Initial Value');

  const updateValue = (newValue) => {
    setValue(newValue);
  };

  return (
    <MyContext.Provider value={{ value, updateValue }}>
      {children}
    </MyContext.Provider>
  );
};

// 使用 Context 提供器包裹组件树
const App = () => {
  return (
    <MyContextProvider>
      <ChildComponent />
    </MyContextProvider>
  );
};

// 在子组件中使用 Context
const ChildComponent = () => {
  const { value, updateValue } = useContext(MyContext);

  const handleButtonClick = () => {
    updateValue('New Value');
  };

  return (
    <div>
      <p>Value: {value}</p>
      <button onClick={handleButtonClick}>Update Value</button>
    </div>
  );
};

在上述示例中,我们创建了一个名为 MyContext 的 Context,并使用 createContext 函数进行创建。然后,我们创建了 MyContextProvider 组件作为 Context 提供器,它使用 useState 钩子来跟踪和更新状态 valueupdateValue 函数用于更新状态的值。

MyContextProvider 组件内部,我们使用 MyContext.Provider 组件来提供状态值 value 和更新函数 updateValue 给其后代组件。通过 value 属性,可以将状态值传递给 ChildComponent 组件。

ChildComponent 中,我们使用 useContext 钩子来访问 Context 的值,获取到了 valueupdateValue。当点击按钮时,调用 updateValue 更新状态值。

这样,通过在提供器组件中使用状态来管理 Context,可以在组件树中的其他组件中访问和更新状态值。注意,在实际应用中,可以根据需要在 Context 中提供更多的状态和函数。

ref callback function

zh-hans.react.dev/learn/manip…

<li
  key={cat.id}
  ref={(node) => {
    const map = getMap();
    if (node) {
      map.set(cat.id, node);
    } else {
      map.delete(cat.id);
    }
  }}
>

在 React 中,ref 是用于访问组件或 DOM 元素的引用。在你提供的代码片段中,ref 是一个函数,它接收一个参数 node,表示对 DOM 元素的引用。

当 React 渲染组件时,会调用 ref 函数两次:

  1. 第一次调用是在组件挂载到 DOM 树上时。此时 node 参数会被传递一个有效的 DOM 元素,表示该组件已经被挂载到 DOM 上。
  2. 第二次调用是在组件从 DOM 树中卸载时。此时 node 参数会被传递 null,表示该组件已经从 DOM 上卸载。

在你的代码中,你利用 ref 函数来维护一个 Map,将 cat.id 和对应的 DOM 元素关联起来。当组件被挂载到 DOM 上时,会将 DOM 元素添加到 Map 中;当组件从 DOM 上卸载时,会从 Map 中删除对应的条目。

需要注意的是,在 React 的生命周期中,组件被卸载时会先调用 ref 函数传递 null,然后再执行其他的卸载操作。因此,ref 函数中的 if (node) 分支表示在组件卸载时会将对应的条目从 Map 中删除。

所以,不会出现没有 node 的情况,因为 React 会确保在卸载组件之前将 ref 函数调用一次并传递 null。这样可以保证在 Map 中正确地添加和删除条目,确保数据的一致性。

useReducer & Context (simple Redux)

在 React 中可以使用自带的 useReducer 和 Context API 来实现 Redux 的基本功能。虽然 Redux 是一个专门的状态管理库,但是基本原理可以在 React 中使用这些原生的功能来实现。

以下是一个简单示例,展示如何使用 useReducer 和 Context 实现 Redux 的基本功能:

首先,创建一个 store.js 文件,定义初始状态和操作函数:

import React, { createContext, useReducer } from 'react';

// 初始状态
const initialState = {
  count: 0,
};

// 操作类型
const ActionTypes = {
  INCREMENT: 'INCREMENT',
  DECREMENT: 'DECREMENT',
};

// 操作函数
const reducer = (state, action) => {
  switch (action.type) {
    case ActionTypes.INCREMENT:
      return { count: state.count + 1 };
    case ActionTypes.DECREMENT:
      return { count: state.count - 1 };
    default:
      return state;
  }
};

// 创建 Context
const StoreContext = createContext();

// 创建 Provider
const StoreProvider = ({ children }) => {
  const [state, dispatch] = useReducer(reducer, initialState);

  return (
    <StoreContext.Provider value={{ state, dispatch }}>
      {children}
    </StoreContext.Provider>
  );
};

export { ActionTypes, StoreContext, StoreProvider };

然后,在应用程序的顶层组件中使用 StoreProvider 包裹组件,并通过 StoreContext.Provider 提供状态和操作函数:

import React from 'react';
import { StoreProvider } from './store';

const App = () => {
  return (
    <StoreProvider>
      {/* 应用程序的其他组件 */}
    </StoreProvider>
  );
};

export default App;

接下来,在需要访问状态或触发操作的组件中使用 StoreContext.ConsumeruseContext 来获取状态和操作函数:

import React, { useContext } from 'react';
import { ActionTypes, StoreContext } from './store';

const Counter = () => {
  const { state, dispatch } = useContext(StoreContext);

  const handleIncrement = () => {
    dispatch({ type: ActionTypes.INCREMENT });
  };

  const handleDecrement = () => {
    dispatch({ type: ActionTypes.DECREMENT });
  };

  return (
    <div>
      <p>Count: {state.count}</p>
      <button onClick={handleIncrement}>Increment</button>
      <button onClick={handleDecrement}>Decrement</button>
    </div>
  );
};

export default Counter;

通过使用 useReducer 来管理状态,然后使用 Context 提供状态和操作函数,可以在 React 中实现类似 Redux 的基本功能。这种方式更适合简单的应用程序,而对于复杂的状态管理和高级功能,Redux 仍然是一个强大的选择。

Context with async request

使用一个全局上下文(context)来存储数据,并在 useEffect 中调用请求并设置该上下文是一种有效的方法。这种方法可以使数据在应用程序中的多个组件之间共享,并确保数据的一致性。 以下是一个示例,展示如何使用全局上下文和 useEffect 来调用异步请求并设置上下文中的数据:

import React, { createContext, useEffect, useState } from 'react';

const DataContext = createContext();

const DataProvider = ({ children }) => {
  const [data, setData] = useState(null);

  useEffect(() => {
    const fetchData = async () => {
      try {
        const response = await fetch('https://api.example.com/data');
        const data = await response.json();

        setData(data);
      } catch (error) {
        console.error('Error fetching data:', error);
      }
    };

    fetchData();
  }, []);

  return (
    <DataContext.Provider value={data}>
      {children}
    </DataContext.Provider>
  );
};

export { DataContext, DataProvider };

在上述示例中,我们创建了一个名为DataContext的全局上下文,并使用useState来存储数据。在useEffect中,我们调用异步请求来获取数据,并使用setData方法设置上下文中的数据。

DataProvider组件将上下文的值设置为当前数据,并将其作为value属性传递给DataContext.Provider。这样,订阅该上下文的所有组件都可以访问到最新的数据。

在其他组件中,可以使用useContext钩子来访问上下文中的数据:

import React, { useContext } from 'react';
import { DataContext } from './DataContext';

const MyComponent = () => {
  const data = useContext(DataContext);

  return (
    <div>
      {data ? (
        <ul>
          {data.map((item) => (
            <li key={item.id}>{item.name}</li>
          ))}
        </ul>
      ) : (
        <p>Loading...</p>
      )}
    </div>
  );
};

在上述示例中,我们使用useContext钩子从DataContext中获取数据,并根据数据是否存在来渲染组件。

通过使用全局上下文和useEffect,可以在应用程序中的任何组件中获取和共享数据,并确保数据的一致性。这种方法非常适用于需要在多个组件之间共享数据的大型应用程序。

React lazy router

React Router 提供了多种路由类型,包括异步路由加载。

异步路由加载可以帮助优化应用程序的性能,特别是在应用程序包含大量页面或组件时。通过按需加载路由组件,可以在需要时动态加载页面,减少初始加载时间并提高用户体验。

下面是使用 React Router v6 中的异步路由加载的示例:

import React, { lazy, Suspense } from 'react';
import { BrowserRouter as Router, Route, Routes } from 'react-router-dom';

// 使用 React.lazy 创建异步加载的组件
const Home = lazy(() => import('./pages/Home'));
const About = lazy(() => import('./pages/About'));

const App = () => {
  return (
    <Router>
      <Suspense fallback={<div>Loading...</div>}>
        <Routes>
          <Route path="/" element={<Home />} />
          <Route path="/about" element={<About />} />
        </Routes>
      </Suspense>
    </Router>
  );
};

export default App;

在上述示例中,我们使用 React.lazy 函数来创建异步加载的组件。通过调用 import() 函数并传递组件的路径,可以动态地加载组件。

在路由配置中,我们使用 <Routes> 组件来定义路由,并使用 <Route> 组件来指定路由的路径和要渲染的组件。在组件属性中使用 element 来指定要渲染的异步加载组件。

通过使用 <Suspense> 组件包裹 <Routes>,我们可以提供一个加载中的占位符,当异步组件加载时显示。在上述示例中,我们使用 <div>Loading...</div> 作为加载中的占位符,但你可以根据需要自定义。

请注意,上述示例使用了 React Router v6 版本的语法。如果你使用的是 React Router v5 或更早版本,可以将 SuspenseRoutes 替换为 SwitchRoute

使用异步路由加载可以根据需要加载组件,提高应用程序的性能和用户体验。当用户访问特定的路由时,相应的组件会在需要时动态加载,并且在加载过程中显示适当的加载中状态。

转载自:https://juejin.cn/post/7249020485471010876
评论
请登录