在我的typescript/react应用程序中(其中设置了strict和strictNullChecks标志),我使用了一些react上下文。
我不能定义默认值,这就是为什么我允许undefined作为值:
const MyMandatoryContext = createContext<string | undefined>(undefined);
我的上下文设置相当高的是组件树(登录用户名,选定,主题,...),并且应该在我想要使用该值的任何地方设置。
为了减少冗余代码,我想安全地检查值的存在。
很容易使用自定义钩子:
const useMyMandatoryContext = ()=>{
const context = useContext(MyMandatoryContext);
if(context === undefined) throw new Error("Must provide a value with MyMandatoryContext.Provider");
return context;
}
这样,我可以使用钩子来检索值,或者完全失败,而不必检查值的存在:
const MyFuncComponent : React.FC =()=>{
const myMandatoryValue = useMyMandatoryContext(); // is string for sure
return (<span>{myMandatoryValue.toUpperCase()}</span>)
}
但是,钩子在类组件中不可用(对于一些遗留代码,或者像ErrorBoundary这样的特定组件,仍然需要)
我可以消费并检查一个值的存在:
class MyClassComponent extends Component {
render () {
return (
<MyMandatoryContext.Consumer>
{(myMandatoryValue) => {
if(!myMandatoryValue) throw new Error("Must provide a value with MyMandatoryContext.Provider");
return <span>{myMandatoryValue.toUpperCase()}</span>;
}}
</MyMandatoryContext.Consumer>
)
}
}
但是我需要在每次我想消费值的时候检查值,这在某种程度上是多余的。
有没有一种方法可以安全地(在类型方面)强制消费者获得不可空的值?
我试图破解React.Consumer
类型,但我没有找到正确的方法:
const MyMandatoryContextConsumer : React.Consumer<string> = ({children})=>{
return children(val=>{
if(val === undefined) throw new Error("Must provide a value with MyMandatoryContext.Provider");
return ???? // Not sure what to do here
})
}
我为repro my needs创建了一个小沙箱:
import "./styles.css";
import { createContext, useContext, useState,Component} from "react";
const MyMandatoryContext = createContext<string | undefined>(undefined);
const useMyMandatoryContext = ()=>{
const context = useContext(MyMandatoryContext);
if(context === undefined) throw new Error("Must provide a value with MyMandatoryContext.Provider");
return context;
}
const MyMandatoryContextConsumer : React.Consumer<string> = ({children})=>{
return children(val=>{
if(val === undefined) throw new Error("Must provide a value with MyMandatoryContext.Provider");
return ???? // Not sure what to do here
})
}
const MyFuncComponent : React.FC =()=>{
const myMandatoryValue = useMyMandatoryContext(); // is string for sure
return (<span>{myMandatoryValue.toUpperCase()}</span>)
}
class MyClassComponent extends Component {
render () {
return (
<MyMandatoryContext.Consumer>
{(myMandatoryValue) => <span>{myMandatoryValue.toUpperCase()}</span>}
</MyMandatoryContext.Consumer>
)
}
}
class MyClassComponent2 extends Component {
render () {
return (
<MyMandatoryContext.Consumer>
{(myMandatoryValue) => {
if(!myMandatoryValue) throw new Error("Must provide a value with MyMandatoryContext.Provider");
return <span>{myMandatoryValue.toUpperCase()}</span>;
}}
</MyMandatoryContext.Consumer>
)
}
}
export default function App() {
const [message, setMessage] = useState<string | undefined>();
return (
<div className="App">
<p>
<input
value={message}
onChange={(evt) => setMessage(evt.target.value)}
/>
</p>
{message && (
<MyMandatoryContext.Provider value={message}>
<MyFuncComponent />
<MyClassComponent />
<MyClassComponent2 />
</MyMandatoryContext.Provider>
)}
</div>
);
}
1条答案
按热度按时间knpiaxh11#
如果你想坚持使用类组件,建议在消费者周围制作一个 Package 器并使用它。
现在你可以在你的课堂上使用它:
Sandbox
注意:children属性是用
string
类型定义的。应该有更好的方法使其通用。