反应被修改的背景
用类似 Redux 的性能和模式反应上下文而不安装 Redux .
* 特性
- 使用您的 React 上下文而不在消费者中附加重接
- 从上下文“ 在飞行上” 读取值的能力 - 在回调中有用, 这样您无需将 UI 绑定在一个上下文值变化中, 只是为了在回调中使用该值
- 类似 Redux 的模式( 减小、 动作和选择器)
- 以类型脚本所建
关于
在这里 空运我们需要一个方法来储存 多个 复杂全局状态的实例( React Context API 做什么),但与 Redux 的性能配合. react-memoized-context 解决了这个问题.
何以不反应背景?
反应上下文提供者 全部 每次是消费者 value 修改 - 即使组件没有在 value (若为物. 这会造成很多的表演问题 社区是 尝试 改为 解开我们研究了这些其他的解决方案,但它们要么还没有准备好,要么有太多的虫子,要么缺乏特征(比如在苍蝇上读取值),所以我们决定自己滚动.
为什么Redux不呢?
Redux 作为全局存储器, 当多个组件想要读取和写入到一个 单人 集中值。 但当你想要 多个 具有相同结构的全局值, Redux 并不那么灵活,因为您需要复制您的还原器、 动作和选择器。 反应上下文是不错的 因为你可以绕着另一个提供商.
安装
npm install --save @air/react-memoized-context
yarn add @air/react-memoized-context
使用量
-
为上下文创建类型 :
- 为要存储的值创建类型 :
export interface User { id: string; name: string; score: number; } export interface UsersTeamContextValue { users: User[]; } - 创建要更新值的行动类型 :
export interface UsersTeamContextActions { addUser: (user: User) => void; assignScore: (userId: User['id'], score: number) => void; } - 为上下文创建类型 - 记得要扩展
MemoizedContextType数字 :export interface UsersTeamContextType extends MemoizedContextType<UsersTeamContextValue>, UsersTeamContextActionsType {} - 为上下文创建默认值 :
export const defaultUsersTeamContextValue: UsersTeamContextType = { ...defaultMemoizedContextValue, getValue: () => ({ users: [], }), addUser: () => {}, assignScore: () => {}, }; - 为您的动作创建类型 - 您将使用它们来修改上下文值 :
export interface AddUserAction extends MemoizedContextAction { type: 'addUser'; data?: { user: User }; } export interface AssignScoreAction extends MemoizedContextAction { type: 'assignScore'; data?: { userId: User['id']; score: number }; } export type UserTeamContextActions = AddUserAction | AssignScoreAction;
- 为要存储的值创建类型 :
-
创建上下文 :
const UsersTeamContext = createContext<UsersTeamContextType>(defaultUsersTeamContextValue); const useUsersTeamContext = () => useContext(UsersTeamContext); -
创建您的调度方法 。 它应该像 reduux 调度一样工作 - 采取行动,修改状态值并返回一个新的状态:
export const usersTeamContextDispatch = (state: UsersTeamContextValue, action: UserTeamContextActions) => { switch (action.type) { case 'assignScore': return { ...state, users: state.users.map((user) => { if (user.id === action.data?.userId) { return { ...user, score: action.data?.score ?? 0, }; } return user; }), }; case 'addUser': return { ...state, users: action.data ? [...state.users, action.data.user] : state.users, }; } }; -
创建您的提供者 :
export const UsersTeamProvider = ({ children }: PropsWithChildren<{}>) => { const { contextValue } = useMemoizedContextProvider<UsersTeamContextValue>( // provide default value for your context { users: [], }, usersTeamContextDispatch, ); // create methods you want to expose to clients const addUser = useCallback((user: User) => contextValue.dispatch({ type: 'addUser', data: { user } }), [contextValue]); const assignScore = useCallback( (userId: User['id'], score: number) => contextValue.dispatch({ type: 'assignScore', data: { userId, score } }), [contextValue], ); // memoize your final value that will be available for clients // just return what's in contextValue and add your methods const value = useMemo<UsersTeamContextType>( () => ({ ...contextValue, addUser, assignScore, }), [addUser, assignScore, contextValue], ); return <UsersTeamContext.Provider value={value}>{children}</UsersTeamContext.Provider>; }; -
要从上下文中获取数据, 您需要选择 :
export const usersTeamUsersSelector = (state: UsersTeamContextValue) => state.users;组件中的用法 :
const context = useUsersTeamContext(); // pass context to useMemoizedContextSelector const users = useMemoizedContextSelector(context, usersTeamUsersSelector);为了简化它,您可以创建一个助手:
export function useUsersTeamContextSelector<T>(selector: (st: UsersTeamContextValue) => T) { const context = useUsersTeamContext(); return useMemoizedContextSelector(context, selector); }那么,来取回
users从上下文可以做到:const users = useUsersTeamContextSelector(usersTeamUsersSelector); -
开始用上下文!
将组件与您的
Provider组件,如同您在 React 语境中所做的:<UsersTeamProvider> <UsersTeam name="Team 1" /> </UsersTeamProvider>
To modify context value, use any of your actions:
```typescript
import { useUsersTeamContextSelector } from "./usersTeamContext";
const { addUser } = useUsersTeamContext()
const onClick = () => {
addUser({ name: 'John' })
}
```
You can read context values on the fly if you need. For example, we will create a user with `users.length` as id. We can use `usersTeamUsersSelector`, but the component would be rerendered every time when any user changes. We don't want that - we need just `users` length. We could create a selector that gets users length, but again - everytime we add a user, the component will rerender. For us, it's enough to know users length by the time we create a user:
```typescript
// get whole context value - it will not cause any rerender!
const contextValue = useUsersTeamContext();
const addNewUser = () => {
// read users array when we need it
const users = contextValue.getValue().users;
// call addUser action to add a new user
contextValue.addUser({ id: users.length + 1, name: userName, score: 0 });
};
```
密码框
您可以使用代码和框播放示例 : https://codesandbox.io/s/boring-morning-j44i9p (中文(简体) )