我在一个全新的Create React App repo中重新创建了错误,源代码可以在这里找到。
正如您在MyContext.tsx中看到的,我只是将useState
的结果添加到我的上下文中。上下文的类型也被定义为元组
const UDContext = createContext<
| undefined
| [
UserData | undefined,
React.Dispatch<React.SetStateAction<UserData | undefined>>
]
>(undefined);
然而,当我尝试通过访问MyComponent.tsx中的数据时
const [userData] = useUserData();
我收到错误
Property 'username' does not exist on type 'UserData | Dispatch<SetStateAction<UserData | undefined>>'.
在我看来,这似乎是在以某种方式将元组的类型组合为一?
有趣的是:下面的代码确实有效(它确实发出了context
可能未定义的警告,这确实有道理(。
const context = useUserData();
const userData = context[0];
MySetter.tsx中也发生了完全相同的情况,其中的确切错误是
Not all constituents of type 'UserData | Dispatch<SetStateAction<UserData | undefined>>' are callable.
上述";解决方案";而不直接破坏在这里也有效。
实现这一点的唯一方法是显式声明useUserData
的返回值的类型。
一些附带说明:
- 这是Create React应用程序的
@next
版本 - 我必须启用
--downlevelIteration
,否则useUserData()
的销毁将产生Type '[UserData | undefined, Dispatch<SetStateAction<UserData | undefined>>] | undefined' is not an array type or a string type. Use compiler option '--downlevelIteration' to allow iterating of iterators.
- 我在CodeSandbox中有一个工作示例,它没有任何问题
这似乎是typescript中的BUG,我发现了两个有类似问题的打开的BUG 31613和BUG 40215
除了您在问题中提到的选项(返回时显式类型(之外,另一个选项是不使用元组析构函数,对于您的代码,它看起来像:
MyComponent.tsx
import React from "react";
import { useUserData } from "./MyContext";
const MyComponent = () => {
const userData = useUserData();
if (!userData) {
return <div>Loading username</div>;
}
return <div>{userData[0]?.username}</div>;
};
export default MyComponent;
MySetter.tsx
import { useEffect } from "react";
import { useUserData } from "./MyContext";
const MySetter = () => {
const userData = useUserData();
// Fakes an API call
useEffect(() => {
setTimeout(() => {
userData && userData[1]({ username: "FooBar" });
}, 750);
});
return null;
};
export default MySetter;
这是因为您的上下文也可能是未定义的,所以您需要进行检查,以确保始终从上下文中返回内容。您可以创建可重复使用的方法来创建总是的东西的上下文,而不是undefined
:
function createCtx<T>() {
const Context = React.createContext<T | undefined>(undefined);
const useCtx = () => {
const ctx = React.useContext(Context);
if (typeof ctx === "undefined") {
throw new Error("useContext must be inside provider with a value.");
}
return ctx;
};
return [useCtx, Context.Provider] as const;
};
然后在可能导出useUserData
和UserDataContext
的地方使用这种方法
const [useUserData, UserDataProvider] = createCtx<[
UserData | undefined,
React.Dispatch<React.SetStateAction<UserData | undefined>>
]>();
const UserDataContext: React.FC = ({ children }) => {
const [userData, setUserData] = React.useState<UserData>();
return (
<UserDataProvider value={[userData, setUserData]}>
{children}
</UserDataProvider>
);
};
并在组件中使用它
export default function App() {
const [userData] = useUserData();
return (
<h2>{userData?.username}</h2>
);
}
这不仅解决了您的问题,还为您提供了一种可重复使用的方法,可以帮助您完成其他您肯定需要的事情。
干杯。