Skip to content

How to handle conditional rendering? #12

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
smashercosmo opened this issue Jun 4, 2018 · 19 comments
Closed

How to handle conditional rendering? #12

smashercosmo opened this issue Jun 4, 2018 · 19 comments

Comments

@smashercosmo
Copy link

Consider the following example:

type Component1Props = { ... }
const Component1: React.SFC<Component1Props> = props => { ... }

type Component2Props = { ... }
const Component2: React.SFC<Component2Props> = props => { ... }

const Component3: React.SFC<???> = props => {
   // some prop from Component1Props
   if(props.prop1) {
      return <Component1 {...props} />
   }
   
   // some prop from Component2Props
   if(props.prop2) {
      return <Component2 {...props} />
   }
}

Ideally I want typechecker to handle two things:

  1. It's required to pass either prop1 or prop2, but not both of them
  2. If prop1 is passed then props are of type Component1Props and if prop2 is passed then props are of type Component2Props

P. S. I'm completely new to TypeScript and I'm not sure that it's even possible)

@ghost
Copy link

ghost commented Jun 4, 2018

I will try to check more but for a quick feedback try:

type IOneOfThem = Component1Props | Component2Props;

@smashercosmo
Copy link
Author

Yeah, that was the first thing that I tried. Didn't work)

@ghost
Copy link

ghost commented Jun 4, 2018

Will try to check this in the next two hours

@tsiq-swyx
Copy link
Contributor

this works for me... does that fit your use case @smashercosmo

type Component1Props = { a1: boolean };
const Component1: React.SFC<Component1Props> = props => <div>{props.a1}</div>;

type Component2Props = { a2: boolean };
const Component2: React.SFC<Component2Props> = props => <div>{props.a2}</div>;

const Component3 = (props: {showprop1: boolean} & (Component1Props | Component2Props)) => {
   // some prop from Component1Props
   const {showprop1, ...rest} = props;
   if (showprop1) {
      return <Component1 {...rest as Component1Props} />;
   } else {
      return <Component2 {...rest as Component2Props} />;
   }
};

@smashercosmo
Copy link
Author

Well, not exactly) Consider real-life example

import React from 'react'
import { NavLink, NavLinkProps } from 'react-router-dom'

type AnchorProps = React.AnchorHTMLAttributes<HTMLAnchorElement>

const Link = (props: ???) => {
   if (props.to) {
      return <NavLink {...props as NavLinkProps} />;
   } else {
      return <a {...props as AnchorProps} />;
   }
};

export default Link;

If user passes 'to' property NavLink should be rendered, if user passes 'href' then simple anchor tag should be rendered, but passing both 'to' and 'href' should result in compilation error). So at least one of two properties (to and href) is required, but passing both of them is forbidden.

P.S. Why aren't you using React.SFC in 3rd component?

@tsiq-swyx
Copy link
Contributor

cos i dont like React.SFC haha

hmm, so you want compile time error if both to and href are passed. this is stumping me.

@ghost
Copy link

ghost commented Jun 4, 2018

I think that is related more to TypeScript than being related to TypeScript-React.

I was busy today.. but I'll invest some time in the next hours

@jpavon
Copy link
Contributor

jpavon commented Jun 4, 2018

This seems to work but it feels too much boilerplate for little gain, maybe there is a better way to do it. (Note that NavLinkProps contains 'href' so you need to omit it to work as you expect)

type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>

interface LinkProps {}

type AnchorProps = React.AnchorHTMLAttributes<HTMLAnchorElement>
type RouterLinkProps = Omit<NavLinkProps, 'href'>

const Link = <T extends {}>(
    props: LinkProps & T extends RouterLinkProps ? RouterLinkProps : AnchorProps
) => {
    if ((props as RouterLinkProps).to) {
        return <NavLink {...props as RouterLinkProps} />
    } else {
        return <a {...props as AnchorProps} />
    }
}

<Link<RouterLinkProps> to="/">My link</Link> // ok
<Link<AnchorProps> href="/">My link</Link> // ok
<Link<RouterLinkProps> to="/" href="/">My link</Link> // error

@tsiq-swyx
Copy link
Contributor

tsiq-swyx commented Jun 4, 2018

wow. i think writing a React component with a generic type like that should definitely be included in this list, i really really like it. i wonder if we can boil this example down a bit simpler though:

// this is just a draft, i dont know what i'm really going for here yet
const Link = <T>(props: LinkProps & T) => {
	return <NavLink {...props} />
}

i realize this doesnt answer OP's original question, im just trying to figure out what i can add to the cheatsheet for this general category of problem. something like a polymorphic react component.

@ghost
Copy link

ghost commented Jun 4, 2018

I used the generic + props merging when I used abstract React Class Components.. it's so powerful

@codepunkt
Copy link
Contributor

Does this help?

interface IComponent1Props {
  foo: string
}

interface IComponent2Props {
  bar: string
}

const Component1: React.SFC<IComponent1Props> = ({ foo }) => (
  <div>component 1: {foo}</div>
)
const Component2: React.SFC<IComponent2Props> = ({ bar }) => (
  <div>component 2: {bar}</div>
)

type Foo = { prop1: true } & IComponent1Props
type Bar = { prop2: true } & IComponent2Props

const isFoo = (test: Foo | Bar): test is Foo => {
  return (test as Foo).prop1 === true
}

const isBar = (test: Foo | Bar): test is Bar => {
  return (test as Bar).prop2 === true
}

const Component3: React.SFC<Foo | Bar> = (props) => {
  if (isFoo(props)) {
    const { prop1, ...rest } = props
    return <Component1 {...rest} />
  } else if (isBar(props)) {
    const { prop2, ...rest } = props
    return <Component2 {...rest} />
  }

  return null
}

@tsiq-swyx
Copy link
Contributor

this is verbose but very readable. didnt know you could do tests like that!

i am going to add a link to this discussion. this has been very helpful to me.

tsiq-swyx added a commit that referenced this issue Jul 11, 2018
@codepunkt
Copy link
Contributor

You're welcome. I was lazy so i didn't name the types well 😆

@smashercosmo
Copy link
Author

@codepunkt you solution can be implemented with much less code using Discriminated Unions, but still it's annoying to specify extra props to differentiate one component from another.

@jpavon solution works, but again, it's annoying to specify generic type every time

I wonder why type guards don't work

import React from 'react'
import {
  Link as ReactRouterLink,
  LinkProps as ReactRouterLinkProps,
} from 'react-router-dom'

type LinkProps =
  | ReactRouterLinkProps
  | React.AnchorHTMLAttributes<HTMLAnchorElement>

function isAnchorProps(
  props: LinkProps,
): props is React.AnchorHTMLAttributes<HTMLAnchorElement> {
  return (
    (props as React.AnchorHTMLAttributes<HTMLAnchorElement>).href !== undefined
  )
}

export function Link(props: LinkProps) {
  if (isAnchorProps(props)) {
    return <a {...props} />
  }

  return <ReactRouterLink {...props} />
}

If I then use Link component like this

<Link href="/hi" to="/hi">
    Hello
</Link>

TypeScript doesn't raise any error

@swyxio
Copy link
Collaborator

swyxio commented May 16, 2019

@azizhk
Copy link
Collaborator

azizhk commented Jul 17, 2019

There is this amazing blog by @andrewbranch on Discriminated Unions:
https://blog.andrewbran.ch/expressive-react-component-apis-with-discriminated-unions/

Can we add this somewhere in the cheat-sheet readme. (Amazing blog, hover over variables in the snippets and see the definition)

There are a few caveats, mentioned here and also in issues microsoft/TypeScript#29703 and microsoft/TypeScript#28131

@ferdaber
Copy link
Collaborator

ferdaber commented Jul 17, 2019

I was very late to this party, but the recommended way of doing conditional rendering is a generic function for function components (or generic constructors for class components), unfortunately that does mean you can't use the SFC annotation. Everyone's already covered what I was going to add 🙃

@swyxio
Copy link
Collaborator

swyxio commented Jul 17, 2019

@azizhk yup i already put it in https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/blob/master/ADVANCED.md#typing-a-component-that-accepts-different-props but its a bit buried. i havent given much thought to how this section can be organized but i hope that people read every bit thoroughly and folow through on the links.

gonna consider this closed for now, open a new issue if people wanna re-ask stuff :)

@swyxio swyxio closed this as completed Jul 17, 2019
@andrewbranch
Copy link

(Thanks for the kind words, @azizhk 😄)

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 pushed 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
holyblock pushed a commit to holyblock/chart that referenced this issue Feb 27, 2023
AIDevMonster added a commit to AIDevMonster/Awesome-React that referenced this issue Jun 21, 2023
whiteghostDev added a commit to whiteghostDev/Awesome-React that referenced this issue Aug 6, 2023
cedev935 pushed a commit to cedev935/React-TypeScript that referenced this issue Sep 11, 2023
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
KonohaBrain125 pushed a commit to KonohaBrain125/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 pushed 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
miracle-soft-expert added a commit to miracle-soft-expert/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 added 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 pushed 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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

8 participants