TypeScript:处理既不引用 state 也不引用 props 的“this”组件属性
TypeScript: dealing with `this` component properties which are reffering neither to state, nor to props
这是我的界面:
export type alphaColorProcessing = (opacity: number) => string | string[];
export interface welcomeMapping {
[key: string]: alphaColorProcessing | string;
}
export interface IPalette {
[key: string]: string | string[] | alphaColorProcessing | welcomeMapping;
}
export interface IThemeContext {
theme: string;
palette: IPalette;
setTheme?: (theme: string) => void;
}
export interface IThemeState extends IThemeContext {
isDark: Boolean;
}
export interface IAppState {
loading: boolean;
themeState: IThemeState;
}
export interface IAppProps {
setTheme?: (theme: string) => void;
}
我在 App
组件中使用这些:
但我遇到了问题:在 this
上声明一个方法时,在构造函数中,据我所知它与 IAppState
无关。所以我的问题是,我如何 declare/use 一个既不引用 State 也不引用 Props 的方法的接口?我在组件的 this
上设置的 methods/properties 需要它。
netInfoSubscription, setTheme
- 是我感兴趣的。
代码如下:
export default class App extends React.PureComponent<{},IAppState> {
showedForce = false;
showedBadIP = false;
constructor(props) {
super(props);
this.setTheme = (theme:string) => {
this.setState((state) => ({
themeState: {
...state.themeState,
theme,
isDark: theme === 'dark',
palette: Palette,
},
}));
};
this.state = {
loading: true,
themeState: {
theme: 'light',
isDark: false,
palette: Palette,
setTheme: this.setTheme,
},
};
}
componentDidMount() {
NetInfo.configure({
reachabilityUrl: 'https://www.cisco.com/',
});
this.netInfoSubscription = NetInfo.addEventListener((state) => {
handleConnectionStatus(state.isConnected);
});
}
render() {
const { themeState, loading } = this.state;
if (loading) return null;
return (
<Provider store={ Store }>
<AppearanceProvider>
<SafeAreaProvider>
<Root>
<ThemeContext.Provider value={ themeState }>
<Navigation />
<FingerprintModal />
</ThemeContext.Provider>
</Root>
</SafeAreaProvider>
</AppearanceProvider>
</Provider>
);
}
}
简单
在构造函数之外编写函数体
您问了两个相关的问题:setTheme
和 netInfoSubscription
。
您有多种选择:
方法语法
你的setTheme
是一个方法,所以你可以把它写成一个,然后在构造函数中使用bind
:
export default class App extends React.PureComponent<{},IAppState> {
constructor(props) {
// ...
this.setTheme = this.setTheme.bind(this);
// ...
}
setTheme(theme: string) {
// ...
}
}
这样做的一个好处是 setTheme
在 App.prototype
上,这有时有助于单元测试(它可以被模拟)。
这适用于 setTheme
,但不适用于 netInfoSubscription
;为此,您需要以下其他选项之一。
属性 声明(包括方法)
你可以把你的setTheme
写成属性(当然,netInfoSubscription
也是属性):
export default class App extends React.PureComponent<{},IAppState> {
setTheme = (theme: string) => {
// ...
};
netInfoSubscription?: SubscriptionType;
// Or:
netInfoSubscription: SubscriptionType | null = null;
}
对于setTheme
,因为这是一个箭头函数,所以不需要绑定。它关闭的上下文 this
引用正在构造的实例。
对于netInfoSubscription
,由于我们在实例构造时没有为它赋值,所以有两种选择:
- 使用
?
表示属性是可选的。
- 使用一个联合类型,其值类似于我们分配的
null
until/unless 我们有订阅。
请注意,其中任何一个都意味着 netInfoSubscription
在代码中的任何地方都可能是 undefined
(或 null
)。对于 你 知道它不是的地方(但 TypeScript 不知道),你可以将它抓取到一个本地常量并做一个断言:
someMethod() {
// If we *know* (from the logic) that we have the subscription now
const { netInfoSubscription } = this;
assertNotNullish(netInfoSubscription);
// ...here you can use `netInfoSubscription`'s value, and TypeScript will understand
// that we now know it has one
// ...
}
assertNotNullish
是我的标准实用函数之一,看起来像这样:
const assertNotNullish: <T>(value: T | undefined | null) => asserts value is T = (value) => {
if (value == null) { // Checks `null` and `undefined`
throw new Error(`Expected value to be non-nullish`);
}
};
注意:您可以只使用non-null assertion operator,但我不会。使用 assertNotNull
之类的东西会 runtime 检查你断言的内容是否正确。使用 non-null 断言运算符不会,它只是告诉 TypeScript 假定断言为真。
一个接口
您可以定义一个接口:
interface AppInstanceMembers {
setTheme: (theme: string) => void;
netInfoSubscription?: SubscriptionType;
// ...
}
// ...
export default class App extends React.PureComponent<{},IAppState> & AppInstanceMembers {
// −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−^^^^^^^^^^^^^^^^^^^^
constructor(props) {
// ...
this.setTheme = (theme/* You don't have to repeat the type here*/) => {
// ...
};
// ...
}
// ...
}
这告诉 TypeScript 在 App
(您在构造函数中满足)上期望 setTheme
(可能还有 netInfoSubscription
)。但这确实意味着您必须重复 setTheme
.
的某些部分
如前所示,在你从逻辑中知道(但 TypeScript 不知道)netInfoSubscription
不会为空的地方,你可以使用 assertNotNullish
.
这是我的界面:
export type alphaColorProcessing = (opacity: number) => string | string[];
export interface welcomeMapping {
[key: string]: alphaColorProcessing | string;
}
export interface IPalette {
[key: string]: string | string[] | alphaColorProcessing | welcomeMapping;
}
export interface IThemeContext {
theme: string;
palette: IPalette;
setTheme?: (theme: string) => void;
}
export interface IThemeState extends IThemeContext {
isDark: Boolean;
}
export interface IAppState {
loading: boolean;
themeState: IThemeState;
}
export interface IAppProps {
setTheme?: (theme: string) => void;
}
我在 App
组件中使用这些:
但我遇到了问题:在 this
上声明一个方法时,在构造函数中,据我所知它与 IAppState
无关。所以我的问题是,我如何 declare/use 一个既不引用 State 也不引用 Props 的方法的接口?我在组件的 this
上设置的 methods/properties 需要它。
netInfoSubscription, setTheme
- 是我感兴趣的。
代码如下:
export default class App extends React.PureComponent<{},IAppState> {
showedForce = false;
showedBadIP = false;
constructor(props) {
super(props);
this.setTheme = (theme:string) => {
this.setState((state) => ({
themeState: {
...state.themeState,
theme,
isDark: theme === 'dark',
palette: Palette,
},
}));
};
this.state = {
loading: true,
themeState: {
theme: 'light',
isDark: false,
palette: Palette,
setTheme: this.setTheme,
},
};
}
componentDidMount() {
NetInfo.configure({
reachabilityUrl: 'https://www.cisco.com/',
});
this.netInfoSubscription = NetInfo.addEventListener((state) => {
handleConnectionStatus(state.isConnected);
});
}
render() {
const { themeState, loading } = this.state;
if (loading) return null;
return (
<Provider store={ Store }>
<AppearanceProvider>
<SafeAreaProvider>
<Root>
<ThemeContext.Provider value={ themeState }>
<Navigation />
<FingerprintModal />
</ThemeContext.Provider>
</Root>
</SafeAreaProvider>
</AppearanceProvider>
</Provider>
);
}
}
简单
在构造函数之外编写函数体
您问了两个相关的问题:setTheme
和 netInfoSubscription
。
您有多种选择:
方法语法
你的setTheme
是一个方法,所以你可以把它写成一个,然后在构造函数中使用bind
:
export default class App extends React.PureComponent<{},IAppState> {
constructor(props) {
// ...
this.setTheme = this.setTheme.bind(this);
// ...
}
setTheme(theme: string) {
// ...
}
}
这样做的一个好处是 setTheme
在 App.prototype
上,这有时有助于单元测试(它可以被模拟)。
这适用于 setTheme
,但不适用于 netInfoSubscription
;为此,您需要以下其他选项之一。
属性 声明(包括方法)
你可以把你的setTheme
写成属性(当然,netInfoSubscription
也是属性):
export default class App extends React.PureComponent<{},IAppState> {
setTheme = (theme: string) => {
// ...
};
netInfoSubscription?: SubscriptionType;
// Or:
netInfoSubscription: SubscriptionType | null = null;
}
对于setTheme
,因为这是一个箭头函数,所以不需要绑定。它关闭的上下文 this
引用正在构造的实例。
对于netInfoSubscription
,由于我们在实例构造时没有为它赋值,所以有两种选择:
- 使用
?
表示属性是可选的。 - 使用一个联合类型,其值类似于我们分配的
null
until/unless 我们有订阅。
请注意,其中任何一个都意味着 netInfoSubscription
在代码中的任何地方都可能是 undefined
(或 null
)。对于 你 知道它不是的地方(但 TypeScript 不知道),你可以将它抓取到一个本地常量并做一个断言:
someMethod() {
// If we *know* (from the logic) that we have the subscription now
const { netInfoSubscription } = this;
assertNotNullish(netInfoSubscription);
// ...here you can use `netInfoSubscription`'s value, and TypeScript will understand
// that we now know it has one
// ...
}
assertNotNullish
是我的标准实用函数之一,看起来像这样:
const assertNotNullish: <T>(value: T | undefined | null) => asserts value is T = (value) => {
if (value == null) { // Checks `null` and `undefined`
throw new Error(`Expected value to be non-nullish`);
}
};
注意:您可以只使用non-null assertion operator,但我不会。使用 assertNotNull
之类的东西会 runtime 检查你断言的内容是否正确。使用 non-null 断言运算符不会,它只是告诉 TypeScript 假定断言为真。
一个接口
您可以定义一个接口:
interface AppInstanceMembers {
setTheme: (theme: string) => void;
netInfoSubscription?: SubscriptionType;
// ...
}
// ...
export default class App extends React.PureComponent<{},IAppState> & AppInstanceMembers {
// −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−^^^^^^^^^^^^^^^^^^^^
constructor(props) {
// ...
this.setTheme = (theme/* You don't have to repeat the type here*/) => {
// ...
};
// ...
}
// ...
}
这告诉 TypeScript 在 App
(您在构造函数中满足)上期望 setTheme
(可能还有 netInfoSubscription
)。但这确实意味着您必须重复 setTheme
.
如前所示,在你从逻辑中知道(但 TypeScript 不知道)netInfoSubscription
不会为空的地方,你可以使用 assertNotNullish
.