使用useContext
实现全局store
React
中实现全局store
的库有redux
等,不过本人更倾向于使用React
自身的方法来实现
简单介绍
使用React.createContext
方法定义一个Context对象
,比如AppContext
。
在App
组件内使用AppContext.Provider
来为AppContext
提供值。
定义useXxxStore
的hook
作为全局store
,内部可以定义数据与方法,并通过返回数据或方法使外部可以查询与修改全局store
。
在App
组件中使用此hook
,并将结果作为AppContext
的一个属性值。
在App
组件的所有子组件中均可以使用AppContext
并获取store
示例代码
1、定义usePersonStore
,目录为src/store/persons.store.ts
import { STORE_KEYS } from '@/configs/store.config';
import { Person } from '@/types/persons';
import { useLocalStorageState } from 'ahooks';
import { useCallback, useMemo } from 'react';
export function usePersonsStore() {
const [persons, setPersons] = useLocalStorageState<Person[]>(
STORE_KEYS.PERSONS_STORE,
{
defaultValue: [],
},
);
const showPersons = useMemo(
() => persons.filter(showPerson => !showPerson.archived),
[persons],
);
function addPerson(person: Person) {
setPersons([...persons, person]);
}
function removePerson(person: Person) {
person.archived = true;
setPersons([...persons]);
}
function mutatePerson(person: Person) {
const target = persons.find(p => p.id === person.id);
if (target) {
Object.assign(target, person);
setPersons([...persons]);
}
}
const getPersonById = useCallback(
function (id: string | undefined) {
if (!id) return undefined;
return persons.find(p => p.id === id);
},
[persons],
);
const getPersonsByIds = useCallback(
(ids: (string | undefined)[]) => ids.map(getPersonById),
[getPersonById],
);
return {
showPersons,
addPerson,
removePerson,
mutatePerson,
getPersonById,
getPersonsByIds,
};
}
2、定义AppContext
,目录为src/store/index.context.ts
import React from 'react';
import { useGameStore } from './game.store';
import { useHistoryGamesStore } from './historyGames.store';
import { useMessageStore } from './message.store';
import { usePersonsStore } from './persons.store';
export const AppContext = React.createContext<{
personStore: ReturnType<typeof usePersonsStore>;
gameStore: ReturnType<typeof useGameStore>;
messageStore: ReturnType<typeof useMessageStore>;
historyGamesStore: ReturnType<typeof useHistoryGamesStore>;
}>(undefined as any);
export function useAppContext() {
const personStore = usePersonsStore();
const gameStore = useGameStore();
const messageStore = useMessageStore();
const historyGamesStore = useHistoryGamesStore();
return { personStore, gameStore, messageStore, historyGamesStore };
}
3、在App.tsx
中使用AppContext
import { useRoutes } from 'react-router-dom';
import MessageHint from './components/MessageHint';
import { routesConfig } from './router.config';
import { AppContext, useAppContext } from './store/index.context';
function App() {
const routes = useRoutes(routesConfig);
const appContextValue = useAppContext();
return (
<AppContext.Provider value={appContextValue}>
{routes}
{appContextValue.messageStore.showMessage && <MessageHint />}
</AppContext.Provider>
);
}
export default App;
4、在子组件
中使用store
,以一个消息提示组件
为例
import React, { useCallback, useContext, useMemo } from 'react';
import classes from './index.module.css';
import calssnames from 'classnames';
import { AppContext } from '@/store/index.context';
export default function MessageHint() {
const { messageStore } = useContext(AppContext);
const classNames = useMemo(() => {
return calssnames({
[classes.MessageHint]: true,
animate__animated: true,
animate__slideInDown: messageStore.showMessage,
animate__slideOutUp: messageStore.startHide,
});
}, [messageStore]);
const hanldeEnd = useCallback(() => {
if (messageStore.startHide) {
messageStore.setStartHide(false);
messageStore.setShowMessage(false);
}
}, [messageStore]);
return (
<div className={classNames} onAnimationEnd={hanldeEnd}>
{messageStore.message}
</div>
);
}