React notes
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
来创建状态 state
和 dispatch
函数。我们定义了两个处理函数 handleIncrement
和 handleDecrement
,并在调用 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
钩子来跟踪和更新状态 value
。updateValue
函数用于更新状态的值。
在 MyContextProvider
组件内部,我们使用 MyContext.Provider
组件来提供状态值 value
和更新函数 updateValue
给其后代组件。通过 value
属性,可以将状态值传递给 ChildComponent
组件。
在 ChildComponent
中,我们使用 useContext
钩子来访问 Context 的值,获取到了 value
和 updateValue
。当点击按钮时,调用 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
函数两次:
- 第一次调用是在组件挂载到 DOM 树上时。此时
node
参数会被传递一个有效的 DOM 元素,表示该组件已经被挂载到 DOM 上。 - 第二次调用是在组件从 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.Consumer
或 useContext
来获取状态和操作函数:
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 或更早版本,可以将 Suspense
和 Routes
替换为 Switch
和 Route
。
使用异步路由加载可以根据需要加载组件,提高应用程序的性能和用户体验。当用户访问特定的路由时,相应的组件会在需要时动态加载,并且在加载过程中显示适当的加载中状态。
转载自:https://juejin.cn/post/7249020485471010876