0

What is the correct way to define default prop values for functional React components?

Using TypeScript

interface ThingProps {
    something?: number;
}

const Thing: FC<ThingProps> = (props: ThingProps): ReactElement => {
    const something = props.something || 0    
    // ...
}

lets me write either

<Thing />

or

<Thing something={someValue} />

both of which work correctly.

Is this the correct idiom, or does React have a different preferred way to achieve this?


Note that while there are answers here about this they are quite old (2016) and none work for me: they result in TS errors with <Thing /> (missing required property) or on uses of something (could be undefined).

orome
  • 45,163
  • 57
  • 202
  • 418
  • 2
    Possible duplicate of [Default property value in React component using TypeScript](https://stackoverflow.com/questions/37282159/default-property-value-in-react-component-using-typescript) – Emile Bergeron Aug 19 '19 at 20:16
  • @EmileBergeron No, I saw that, but the question here is about this specific approach. That answer is from 2016 and things have evolved. The [answer there](https://stackoverflow.com/a/37282264/656912) doesn't work for me. – orome Aug 19 '19 at 20:19
  • Then please make sure to highlight what's not working in the other question in your question, like mentioning the TS version you're using. Or put a bounty on the old question to gather up to date answers. – Emile Bergeron Aug 19 '19 at 20:28
  • The [last answer](https://stackoverflow.com/a/54479670/1218980) looks like it has what you're missing? – Emile Bergeron Aug 19 '19 at 20:34
  • Specifically the `type PropsWithDefaults = Props & DefaultProps;` – Emile Bergeron Aug 19 '19 at 20:34
  • @EmileBergeron Sorry, maybe the question isn't clear. It's about whether there is a reason not to just do this. Why chose one of those other approaches? – orome Aug 19 '19 at 20:36
  • _"Is this the correct idiom"_ Since you're not defining `defaultProps` and doing your own defaults with `const something = props.something || 0`, you're bypassing React's prop types validation, which works but loses the benefits. – Emile Bergeron Aug 19 '19 at 20:37
  • I'm not using TS, there are probably other reasons, like catching an accidental `undefined` prop value. – Emile Bergeron Aug 19 '19 at 20:39
  • @EmileBergeron Ah, so the issue here is that I'm ducking under React with TypeScript. (The `PropsWithDefaults` approach has that problem, too, no?) – orome Aug 19 '19 at 20:41
  • [The `PropsWithDefaults` answer](https://stackoverflow.com/a/54479670/1218980) defines both interfaces, then merges them into a third type, making it possible to define the `defaultProps` (on his class component example, but similar on a function component). He's then benefiting from both TS and React. – Emile Bergeron Aug 19 '19 at 20:46
  • 1
    It looks like [there's no definitive way of doing it](https://github.com/DefinitelyTyped/DefinitelyTyped/issues/11640) right now. – Emile Bergeron Aug 19 '19 at 20:49
  • 2
    @EmileBergeron Hmmm, indeed! For now, it seems like I can get the best of both worlds by doing what I'm doing (simple TypeScript that's easy to follow and avoids introducing new types/interfaces) and simply add `Counter.defaultProps = { sequence: 0 }` which tells React what I'm up to. – orome Aug 19 '19 at 21:12

2 Answers2

1

You can set them directly on Thing:

Thing.defaultProps = {
  something: 0
};
narmageddon
  • 1,117
  • 2
  • 10
  • 7
  • Do I get anything from that that I don't get just using TypeScript? The issue I'm having is that not only does TypeScript on it's own seem to be able to handle this in the obvious way, but `defaultProps` (as well as the other methods mentioned in the 2016 answers) aren't recognized by TypeScript. – orome Aug 19 '19 at 20:29
  • 1
    Let’s say `something` was required and not optional like you have it… If a JavaScript app was pulling in that component, your `` would set `something` to 0 and wouldn’t throw an error if you left off the prop. But if you are using that `` component in a TypeScript app, it would still throw an error because `` expects `something` to be a `number`. I’ll set my defaultProps in a project like a component library, where I’m not sure what type of app is consuming the component. – narmageddon Aug 19 '19 at 20:36
  • Would doing what I've got above and [then adding `Thing.defaultProps`](https://stackoverflow.com/questions/57562960/how-do-i-establish-default-props-for-functional-react-components?noredirect=1#comment101589648_57562960) cover me? – orome Aug 19 '19 at 21:13
0

You can simply assign the props parameter to a default value (or object with values) in the function declaration:

interface ThingProps {
  something?: number;
}

const Thing: React.FC<ThingProps> = (props = {something: 1337}) => {
  return <>{props.something}</>
}

Or use the spread operator which I personally find a bit cleaner:

interface ThingProps {
  something?: number;
}

const Thing: React.FC<ThingProps> = ({something = 1337}) => {
  return <>{something}</>
}

If you don't need to reuse the interface ThingProps elsewhere you can even do everything inline:

const Thing: React.FC<{ something?: number; }> = ({something = 1337}) => {
  return <>{something}</>
}

BTW I'm pretty sure doing

const Thing: FC<ThingProps> = (props: ThingProps): ReactElement => {

is redundant vs just doing

const Thing: FC<ThingProps> = (props) => {

because you've already typed Thing to be a React functional component and typed the props to be ThingProps in the variable declaration.

jered
  • 11,220
  • 2
  • 23
  • 34