我用一个典型的HOC包装了一个组件,在其他非TS项目中以某种格式使用了该HOC。我的问题是我无法在包装好的组件内部使用HOC的prop currentBreakpoint
,因为它希望它属于组件的类型:
Property 'currentBreakpoint' does not exist on type 'Readonly<OwnProps> & Readonly<{ children?: ReactNode; }>'. TS2339
因此,我随后导入了HOC的props接口,并将其合并到组件自己的prop接口中,但是我得到了:
Property 'currentBreakpoint' is missing in type '{}' but required in type 'Readonly<Props>'. TS2741
我希望在我看来调用currentBreakpoint
时会定义属性<ChildComponent />
,即使它是由HOC提供的。
-
这是我的文件:
react-scripts@3.0.1
typescript@3.4.5
ChildComponent.js
import React, { Component } from 'react';
import withBreakpoints, {
Breakpoints,
Props as WithBreakpointsProps
} from 'lib/withBreakpoints';
interface OwnProps {}
type Props = WithBreakpointsProps & OwnProps;
class ChildComponent extends Component<Props> {
constructor(props: Props) {
super(props);
this.state = {
daysPerPage: this.getDaysPerPageFromViewportSize(),
cursor: new Date()
};
}
getDaysPerPageFromViewportSize = () => {
// the problem area
const { currentBreakpoint } = this.props;
let daysPerPage;
switch (currentBreakpoint) {
case Breakpoints.SMALL.label:
case Breakpoints.EXTRA_SMALL.label:
daysPerPage = 1;
break;
case Breakpoints.MEDIUM.label:
case Breakpoints.LARGE.label:
daysPerPage = 4;
break;
case Breakpoints.EXTRA_LARGE.label:
daysPerPage = 6;
break;
default:
daysPerPage = 1;
break;
}
return daysPerPage
};
render() {
return (
<div className="AvailabilityCalendar" />
);
}
}
export default withBreakpoints<Props>(AvailabilityCalendar);
withBreakpoints.ts
import React, { Component, ComponentType } from 'react';
export type CurrentBreakpoint = string | null;
export interface Props {
currentBreakpoint: string
}
export interface Breakpoint {
label: string;
lowerBound: number;
upperBound: number;
}
export interface State {
currentBreakpoint: CurrentBreakpoint;
}
export const Breakpoints: {
[id: string]: Breakpoint
} = {
EXTRA_SMALL: {
label: 'EXTRA_SMALL',
lowerBound: 0,
upperBound: 640
},
SMALL: {
label: 'SMALL',
lowerBound: 641,
upperBound: 1024
},
MEDIUM: {
label: 'MEDIUM',
lowerBound: 1025,
upperBound: 1280
},
LARGE: {
label: 'LARGE',
lowerBound: 1281,
upperBound: 1920
},
EXTRA_LARGE: {
label: 'EXTRA_LARGE',
lowerBound: 1921,
upperBound: 1000000
}
};
const withBreakpoints = <WrappedComponentProps extends object>(
WrappedComponent: ComponentType<WrappedComponentProps>
) => {
class WithBreakpoints extends Component<WrappedComponentProps, State> {
constructor(props: WrappedComponentProps) {
super(props);
this.state = {
currentBreakpoint: this.getCurrentBreakpoint()
};
}
componentDidMount() {
window.addEventListener('resize', this.checkBreakpoints);
}
componentWillUnmount() {
window.removeEventListener('resize', this.checkBreakpoints);
}
checkBreakpoints = () => {
let currentBreakpoint: CurrentBreakpoint = this.getCurrentBreakpoint();
if (currentBreakpoint !== this.state.currentBreakpoint) {
this.setState({ currentBreakpoint });
}
};
getCurrentBreakpoint = (): CurrentBreakpoint => {
const currentViewportWidth: number = Math.round(window.innerWidth);
return Object.keys(Breakpoints).find(
key =>
Breakpoints[key].lowerBound < currentViewportWidth &&
Breakpoints[key].upperBound > currentViewportWidth
) || null;
};
render() {
return (
<WrappedComponent
{...this.props as WrappedComponentProps}
currentBreakpoint={this.state.currentBreakpoint}
/>
);
}
}
return WithBreakpoints;
};
export default withBreakpoints;
-
currentBreakpoint
上使ChildComponent
为可选”。我已将其视为其他问题的公认答案,但我认为我们都可以同意这是对属性上可选标志的不当使用,并且违反了使用Typescript的目的。
答案 0 :(得分:1)
您应该阅读有关ts泛型的信息。
例如,假设一个组件具有外部道具,如type OwnProps = { a: string }
,而HOC注入了type InjectedProps = { b: boolean }
。这意味着该组件的最终道具将是type Props = OwnProps & InjectedProps
。
但是,如果您这样做:
const Component = (props: Props) => {}
export default hoc(Component);
并且尝试仅通过a
道具使用该组件,当该道具实际上在内部被接收时,它将抱怨没有收到b
道具。
您可以做的事情是这样的:
const Component = (props: Props) => {}
export default hoc<OwnProps>(Component);
以便特设组件知道其需要的外部道具以及内部接收的道具。
有了这一点,就需要对HOC定义及其所接收的泛型进行一些调整,以使其具有适当的最终Props
。
答案 1 :(得分:1)
要扩展@cfraser的答案,注入内部道具的HoC的常见模式是键入您的HoC
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<skipTests>true</skipTests>
</configuration>
</plugin>
即// these will be injected by the HoC
interface SomeInternalProps {
someProp: any
}
function hoc<P>(Wrapped: React.Component<P & SomeInternalProps>): React.Component<Omit<P, keyof SomeInternalProps>>
期望一个包含道具hoc
和P
的组件,并返回一个仅期望SomeInternalProps
的组件。
您甚至可以省略泛型参数,TS会弄清楚。例如
P
有关function SomeComponent(props: { prop: any } & SomeInternalProps) {...}
export default hoc(SomeComponent) // Component<{ prop: any }>
的信息,请参见here