Skip to content

Hoc example that supports default props of wrapped component #86

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

Closed
ferdaber opened this issue Feb 8, 2019 · 14 comments
Closed

Hoc example that supports default props of wrapped component #86

ferdaber opened this issue Feb 8, 2019 · 14 comments
Labels
ADVANCED Advanced Cheatsheet HOC HOC Cheatsheet wontfix This will not be worked on

Comments

@ferdaber
Copy link
Collaborator

ferdaber commented Feb 8, 2019

Cc @brieb

@ferdaber
Copy link
Collaborator Author

ferdaber commented Feb 9, 2019

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)

@swyxio
Copy link
Collaborator

swyxio commented Feb 11, 2019

love it. will do some writing up some time this week. may need a whole cheatsheet just for HOCs, ha.

@ferdaber
Copy link
Collaborator Author

Related issue opened in TS repo: microsoft/TypeScript#29873

@infctr
Copy link

infctr commented Feb 18, 2019

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!

@infctr
Copy link

infctr commented Feb 18, 2019

How about using with react-redux' connect? Typechecking here gets tricky

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
}

@ferdaber
Copy link
Collaborator Author

If you're just wrapping something with a connect call you should just simplify it:

function withTheme<P extends WithThemeProps>(component: React.ComponentType<P>) {
  return connect(() => ({ theme: 'theme' }))(component)
}

@infctr
Copy link

infctr commented Feb 18, 2019

I'm getting same error tho

Argument of type 'ComponentType<P>' is not assignable to parameter of type 'ComponentType<Matching<WithThemeProps & DispatchProp<AnyAction>, P>>'.
  Type 'ComponentClass<P, any>' is not assignable to type 'ComponentType<Matching<WithThemeProps & DispatchProp<AnyAction>, P>>'.
    Type 'ComponentClass<P, any>' is not assignable to type 'ComponentClass<Matching<WithThemeProps & DispatchProp<AnyAction>, P>, any>'.
      Type 'P' is not assignable to type 'Matching<WithThemeProps & DispatchProp<AnyAction>, P>'.
        Type 'WithThemeProps' is not assignable to type 'Matching<WithThemeProps & DispatchProp<AnyAction>, P>'.ts(2345)

@infctr
Copy link

infctr commented Feb 18, 2019

@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

@ferdaber
Copy link
Collaborator Author

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.

@stale
Copy link

stale bot commented Aug 20, 2020

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!

@stale stale bot added the wontfix This will not be worked on label Aug 20, 2020
@swyxio swyxio added ADVANCED Advanced Cheatsheet HOC HOC Cheatsheet and removed wontfix This will not be worked on labels Aug 22, 2020
@stale
Copy link

stale bot commented Oct 21, 2020

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!

@stale stale bot added the wontfix This will not be worked on label Oct 21, 2020
@swyxio swyxio closed this as completed in f7dbf8f Oct 21, 2020
@bozdoz
Copy link
Contributor

bozdoz commented Apr 20, 2021

I think I finally got a working version of this:

TS Playground

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" />

@swyxio
Copy link
Collaborator

swyxio commented Apr 23, 2021

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

bernssolg added a commit to bernssolg/My-React-Sample that referenced this issue Feb 28, 2022
erinodev added a commit to erinodev/My-React-project that referenced this issue Feb 28, 2022
petardev101 added a commit to petardev101/react that referenced this issue Jun 4, 2022
supercrytoking added a commit to supercrytoking/react that referenced this issue Jul 14, 2022
kevindavies8 added a commit to kevindavies8/react-full-stack-developer that referenced this issue Aug 24, 2022
johnfrench3 pushed a commit to johnfrench3/react-Fronted-developer that referenced this issue Sep 7, 2022
ericbrown2716 added a commit to ericbrown2716/react-stack-build-website that referenced this issue Sep 29, 2022
peterjohnson4987 added a commit to peterjohnson4987/full-stack-developer-react that referenced this issue Oct 3, 2022
renawolford6 pushed a commit to renawolford6/react-husky-website that referenced this issue Oct 6, 2022
whiteghostDev added a commit to whiteghostDev/Awesome-React that referenced this issue Aug 6, 2023
cedev935 added a commit to cedev935/React-TypeScript that referenced this issue Sep 11, 2023
@alexeyr-ci
Copy link

@bozdoz The TypeScript playground example doesn't seem to compile anymore.

aleksandaralek added a commit to aleksandaralek/typescript-react-cheatsheet that referenced this issue Oct 24, 2023
xbucks pushed a commit to xbucks/react-cheatsheets that referenced this issue Oct 24, 2023
joyfulmagician added a commit to joyfulmagician/react that referenced this issue Oct 25, 2023
greenback99 pushed a commit to greenback99/React-Typescript that referenced this issue Oct 26, 2023
TOP-10-DEV added a commit to TOP-10-DEV/typescript-cheatsheets-react that referenced this issue Dec 8, 2023
secretsuperstar1109 added a commit to secretsuperstar1109/react-typescript-cheatsheets that referenced this issue Dec 9, 2023
champion119 added a commit to champion119/react that referenced this issue Jan 5, 2024
dragon360-dev added a commit to dragon360-dev/react that referenced this issue Mar 13, 2024
EugeneYoona added a commit to EugeneYoona/React_full_src that referenced this issue Apr 10, 2024
fairskyDev0201 added a commit to fairskyDev0201/typescript-cheatsheet that referenced this issue Apr 17, 2024
solutionGuru0129 added a commit to solutionGuru0129/react that referenced this issue May 27, 2024
alisenola added a commit to alisenola/react-cheatsheets that referenced this issue May 29, 2024
marceloaguilera94 pushed a commit to marceloaguilera94/react that referenced this issue Jun 5, 2024
Linda423 added a commit to Linda423/React that referenced this issue Jul 31, 2024
genie4viz pushed a commit to genie4viz/react that referenced this issue Sep 2, 2024
dreamsoft07 pushed a commit to dreamsoft07/React-TypeScript that referenced this issue Oct 15, 2024
chivalrousdev added a commit to chivalrousdev/React that referenced this issue Nov 14, 2024
super-dev03 pushed a commit to super-dev03/react-test that referenced this issue Dec 25, 2024
Mani2044 added a commit to Mani2044/react that referenced this issue Feb 6, 2025
sunstrike-spec added a commit to sunstrike-spec/react that referenced this issue Feb 23, 2025
darkhorse374 added a commit to darkhorse374/React that referenced this issue Feb 28, 2025
Misha0421 added a commit to Misha0421/React-Project that referenced this issue Mar 4, 2025
luckyman0026 added a commit to luckyman0026/react that referenced this issue Mar 5, 2025
buddy0323 added a commit to buddy0323/react that referenced this issue Mar 11, 2025
ronanmurphy26 added a commit to ronanmurphy26/react that referenced this issue Mar 13, 2025
reo0603 added a commit to reo0603/TypeScript-Cheatsheet that referenced this issue Apr 10, 2025
Antoon-tech added a commit to Antoon-tech/Creating-Sheet that referenced this issue May 18, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
ADVANCED Advanced Cheatsheet HOC HOC Cheatsheet wontfix This will not be worked on
Projects
None yet
Development

No branches or pull requests

5 participants