New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Hoc example that supports default props of wrapped component #86
Comments
working example: import React from 'react'
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>
export interface WithThemeProps {
theme: {}
}
interface AddlProps {
foo: string
}
function withTheme<C extends React.JSXElementConstructor<React.ComponentProps<C> & WithThemeProps>>(
Component: C
) {
type OuterProps = JSX.LibraryManagedAttributes<
C,
Omit<React.ComponentProps<C>, keyof WithThemeProps>
> &
AddlProps
class WithTheme extends React.Component<OuterProps> {
render() {
return (
<Component
{...this.props as JSX.LibraryManagedAttributes<C, React.ComponentProps<C>>}
// this is bad below because `theme` is not typechecked against inner component
theme={1}
/>
)
}
}
return WithTheme
}
function withTheme<C extends React.JSXElementConstructor<React.ComponentProps<C> & WithThemeProps>>(
Component: C
) {
type OuterProps = JSX.LibraryManagedAttributes<C, Omit<React.ComponentProps<C>, 'theme'>> &
AddlProps
class WithTheme extends React.Component<OuterProps> {
render() {
const themeProps: WithThemeProps = {
theme: '',
}
return <Component {...this.props as any} {...themeProps} />
}
}
return WithTheme
}
interface MyComponentProps extends WithThemeProps {
message: string
}
class MyComponent extends React.Component<MyComponentProps> {
static defaultProps = {
message: '',
}
render() {
return <div />
}
}
interface MyBadComponentProps {
message: string
}
class MyBadComponent extends React.Component<MyBadComponentProps> {
static defaultProps = {
message: '',
}
render() {
return <div />
}
}
const Test = withTheme(MyComponent)
const testEl = <Test />
const FailTest = withTheme(MyBadComponent) |
love it. will do some writing up some time this week. may need a whole cheatsheet just for HOCs, ha. |
Related issue opened in TS repo: microsoft/TypeScript#29873 |
cleaner? function withTheme<
C extends React.ComponentType<React.ComponentProps<C> & WithThemeProps>,
ResolvedProps = JSX.LibraryManagedAttributes<C, Omit<React.ComponentProps<C>, keyof WithThemeProps>>
>(
Component: C
) {
return class WithTheme extends React.Component<AddlProps & ResolvedProps> {
render() {
const themeProps: WithThemeProps = {
theme: '',
}
return (
<Component
{...this.props as JSX.LibraryManagedAttributes<C, React.ComponentProps<C>>}
{...themeProps}
/>
);
}
}
} @ferdaber thanks for the valid typecast hint! |
How about using with react-redux' function withTheme<
C extends React.ComponentType<React.ComponentProps<C> & WithThemeProps>,
ResolvedProps = JSX.LibraryManagedAttributes<C, Omit<React.ComponentProps<C>, keyof WithThemeProps>>
>(
Component: C
) {
class WithTheme extends React.Component<WithThemeProps & AddlProps & ResolvedProps> {
render() {
const { theme, ...props } = this.props;
return (
<Component
{...props as JSX.LibraryManagedAttributes<C, React.ComponentProps<C>>}
theme={theme}
/>
);
}
}
return connect<WithThemeProps, void, AddlProps & ResolvedProps>(
() => ({ theme: 'theme' })
)(WithTheme) // error, types don't match
} |
If you're just wrapping something with a function withTheme<P extends WithThemeProps>(component: React.ComponentType<P>) {
return connect(() => ({ theme: 'theme' }))(component)
} |
I'm getting same error tho
|
@ferdaber And besides this loses inner component's default props import React from 'react'
import { compose } from 'redux'
import { connect } from 'react-redux'
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>
// wrapper
export interface WithThemeProps {
theme: {}
}
interface AddlProps {
foo: string
}
function withTheme<P extends WithThemeProps>(component: React.ComponentType<P>) {
return connect<WithThemeProps, void, AddlProps & Omit<P, keyof WithThemeProps>>(() => ({ theme: 'theme' }))(component as React.ComponentType)
}
// component
interface MyComponentProps extends WithThemeProps {
message: string
required: string
}
class MyComponent extends React.Component<MyComponentProps> {
static defaultProps = {
message: '',
}
render() {
return <div />
}
}
const Test = withTheme(MyComponent)
const testEl = <Test required="123" foo='123' /> // error, message is missing Another issue, not sure how it's related, but I've ran into this playing with HOC's and default props // use with `compose` from redux
const Composed = compose(withTheme)(MyComponent)
const x = <Composed required="123" ></Composed> // error, all component's props are lost |
I can play around with it, react-redux's types rely heavily on conditional types which don't get inferred very well, I'll take a look. |
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions! |
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions! |
I think I finally got a working version of this: I used @infctr 's example: import React from 'react';
export interface WithThemeProps {
theme: string
}
function withTheme<
C extends React.ComponentType<React.ComponentProps<C> & WithThemeProps>,
ResolvedProps = JSX.LibraryManagedAttributes<C, Omit<React.ComponentProps<C>, keyof WithThemeProps>>
>(
Component: C
) {
return class WithTheme extends React.Component<ResolvedProps> {
render() {
const themeProps: WithThemeProps = {
theme: '',
}
return (
<Component
{...this.props as JSX.LibraryManagedAttributes<C, React.ComponentProps<C>>}
{...themeProps}
/>
);
}
}
}
class Button extends React.Component<{size: string, color: string} & WithThemeProps> {
static defaultProps = {
size: 'm'
}
}
const ThemedButton = withTheme(Button);
// Does not work: missing theme
const a = () => <Button color="red" />
// Works: theme provided by HOC
const b = () => <ThemedButton color="red" /> |
nice work @bozdoz - if you spot a way to add it back to the main docs to help others, please be my guest, this is a shared thing |
@bozdoz The TypeScript playground example doesn't seem to compile anymore. |
Cc @brieb
The text was updated successfully, but these errors were encountered: