reactjs - 如何在组件中使用 useHook,而不是将其返回的值作为 prop 传递给使用 react 和 typescript 的组件?
问题描述
我想在组件本身中使用 useHook,而不是使用 react 和 typescript 将其作为道具传递给每个组件。
我想做什么?我有一个名为 useRefresh 的 useHook,它返回 isLoading 状态。此 isLoading 状态用于在每个页面中显示加载指示器。
所以我有三个页面,只要这个 isLoading 为真,就应该在这些页面中显示一个加载指示器。
下面是我的代码,
function App(){
const user = useGetUser();
return (
<Router>
<Switch>
<Route
path="/"
render={props: any => (
user ? (<Main {...props} />) : (
<LoginPage/>
);
)}
</Route>
</Switch>
</Router>
);
}
export function useLoad() {
const { refetch: refetchItems } = useGetItems();
const { refetch: refetchOwnedItems } = useListOwnedItems();
return async function() {
await refreshCompany();
refetchItems();
refetchOwnedItems();
};
}
function useAnother(Id: string) {
const [compId, setCompId] = React.useState(undefined);
const [isLoading, setIsLoading] = React.useState(false);
const comp = useCurrentComp(Id);
const load = useLoad();
if (comp && comp.id !== compId) {
setCompId(comp.id);
const prevCompId = compId !== undefined;
if (prevCompId) {
setIsLoading(true);
load().then(() => {
setIsLoading(false);
});
}
}
}
function Main ({user}: Props) {
useAnother(user.id);
return (
<Router>
<Switch>
<Route
path="/"
render={routeProps => (
<FirstComp {...routeProps} />
)}
/>
<Route
path="/items"
render={routeProps => (
<SecondComp {...routeProps} />
)}
/>
//many other routes like these
</Switch>
</Router>
);
}
function FirstComp () {
return(
<Wrapper>
//some jsx
</Wrapper>
);
}
function SecondComp () {
return(
<Wrapper>
//some jsx
</Wrapper>
);
}
现在我想将 isLoading 状态传递给 Main 组件中的每个组件......所以我像下面这样传递了它,
function Main ({user}: Props) {
const isLoading = useAnother(user.id); //fetching isLoading here from useHook
return (
<Router>
<Switch>
<Route
path="/"
render={routeProps => (
<FirstComp isLoading={isLoading} {...routeProps} />
)}
/>
<Route
path="/items"
render={routeProps => (
<SecondComp isLoading={isLoading} {...routeProps} />
)}
/>
//many other routes like these
</Switch>
</Router>
);
}
function FirstComp ({isLoading}: Props) {
return(
<Wrapper>
displayIndicatorWhen(isLoading);
//some jsx
</Wrapper>
);
}
function SecondComp ({isLoading}: Props) {
return(
<Wrapper>
displayIndicatorWhen(isLoading);
//some jsx
</Wrapper>
);
}
这行得通。但对我来说似乎不是正确的方法.. 我不想将此 isLoading 状态作为道具传递给这些组件中的每一个。其中有10多个。
有没有其他方法可以做到这一点。有人可以帮我解决这个问题。谢谢。
解决方案
最常见的解决方案是创建一个包含整个组件树的上下文。此上下文包含您的钩子拉入的状态
////LoadingContext.tsx
const LoadingContext = createContext();
const LoadingContextProvider = () => {
const [isLoading, setIsLoading] = useState(false);
return (
<LoadingContextProvider.Provider
value={{
isLoading,
setIsLoading
}}
/>
)
}
export const useLoading = () => useContext(LoadingContext);
您需要围绕将要调用的任何内容包装上下文useLoading
:
import { LoadingContextProvider } from './LoadingContext' //or wherever this is relative to Main.tsx
<LoadingContextProvider>
<Router>
...(router stuff)
</Router>
</LoadingContextProvider>
现在您可以在较低级别的组件中调用 useLoading。
//in another file defining a lower-level component:
import { useLoading } from '../../LoadingContext' //or wherever the context stuff is relative to this component definition
const FirstComp = () =>
const [isLoading, setIsLoading] = useLoading();
const handleClick = () => {
setIsLoading(true);
callMyApi().then(() => setIsLoading(false));
}
if(isLoading){
return <LoadingGif />
}
else{
return <div onClick={handleClick}>Click me!</div>
}
)}
推荐阅读
- python - 如何使用逗号分隔文件创建字典
- php - 如何从mysql表中检索数据
- arrays - 如何在 Teradata 中将 3 条记录拼合为一条?
- google-cloud-firestore - 如何使用 Firestore 安全规则 resource.id 字段?
- javascript - 具有固定列的可滚动表,固定大小相同
- android - 我可以使用订阅主题而不是维护令牌而没有性能滞后吗
- django - 如何更新数据库Django中的字段?
- bash - 连接 awk 输出、字符串和文本文件
- android-layout - 如何在 nativescript 中使 Label 元素内联?
- sql-server - 如何将数据从多个 T-SQL 查询迁移到单个表?