-
Notifications
You must be signed in to change notification settings - Fork 4.2k
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
Comments
I will try to check more but for a quick feedback try: type IOneOfThem = Component1Props | Component2Props; |
Yeah, that was the first thing that I tried. Didn't work) |
Will try to check this in the next two hours |
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} />;
}
}; |
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? |
cos i dont like React.SFC haha hmm, so you want compile time error if both |
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 |
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 |
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. |
I used the generic + props merging when I used abstract React Class Components.. it's so powerful |
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
} |
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. |
You're welcome. I was lazy so i didn't name the types well 😆 |
@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 |
fyi we've broken out the conditional rendering discussion into a bunch of different sections: https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/blob/master/ADVANCED.md#typing-a-component-that-accepts-different-props try the One or the Other method: https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/blob/master/ADVANCED.md#props-one-or-the-other-but-not-both |
There is this amazing blog by @andrewbranch on 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 |
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 🙃 |
@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 :) |
(Thanks for the kind words, @azizhk 😄) |
Consider the following example:
Ideally I want typechecker to handle two things:
P. S. I'm completely new to TypeScript and I'm not sure that it's even possible)
The text was updated successfully, but these errors were encountered: