154

I would like to know if there is a better way to conditionally pass a prop than using an if-statement.

For example, right now I have:

var parent = React.createClass({
  propTypes: {
    editable: React.PropTypes.bool.isRequired,
    editableOpts: React.PropTypes.shape({...})
  },
  render: function() {
    if(this.props.editable) {
      return (
        <Child editable={this.props.editableOpts} />
      );
    } else {
      // In this case, Child will use the editableOpts from its own getDefaultProps()
      return (
        <Child />
      );
    }
  }
});

Is there a way to write this without the if-statement? I am was thinking something along the lines of a type of inline-if-statement in the JSX:

var parent = React.createClass({
  propTypes: {
    editable: React.PropTypes.bool.isRequired,
    editableOpts: React.PropTypes.shape({...})
  },
  render: function() {
    return (
      <Child 
        {this.props.editable ? editable={this.props.editableOpts} : null} 
      />
    );
  }
});

To wrap-up: I'm trying to find a way to define a prop for Child, but pass a value (or do something else) such that Child still pulls that prop's value from Child's own getDefaultProps().

AmerllicA
  • 29,059
  • 15
  • 130
  • 154
Matthew Herbst
  • 29,477
  • 23
  • 85
  • 128
  • Can you include the code for `Child` as well? Also, did you mean to say `` instead of ``? – Jim Skerritt Aug 26 '15 at 17:21
  • @JimSkerritt I didn't confuse the props, though I know it looks that way. I'm trying to use [`react-bootstrap-table`](http://allenfang.github.io/react-bootstrap-table/example.html) and that is the format that they use. I'm not sure the `Child` code actually matters for what I'm asking, which is why I didn't include it. I'm really just looking for a way to optionally pass or not pass a prop to `Child` that doesn't require having a massive amount of similar code in if-statements in the `Parent`. – Matthew Herbst Aug 26 '15 at 17:24

9 Answers9

213

You were close with your idea. It turns out that passing undefined for a prop is the same as not including it at all, which will still trigger the default prop value. So you could do something like this:

var parent = React.createClass({
  propTypes: {
    editable: React.PropTypes.bool.isRequired,
    editableOpts: React.PropTypes.shape({...})
  },
  render: function() {
    return <Child 
      editable={this.props.editable ?
                  this.props.editableOpts : 
                  undefined}
    />;
  }
});
Jim Skerritt
  • 4,348
  • 2
  • 28
  • 25
  • Oh, awesome! Did you find that in documentation somewhere? I was looking for that but couldn't find anything during a quick search – Matthew Herbst Aug 26 '15 at 17:37
  • not sure if it's in the documentation or not, just something I've learned while using React :) – Jim Skerritt Aug 26 '15 at 17:40
  • 18
    `null` is not armed with such power as `undefined`, BTW. – Season Aug 20 '16 at 10:46
  • 3
    useful trick! wish there were good resources on how to effectively use conditional rendering techniques – nicodjimenez Sep 22 '17 at 03:21
  • 1
    In the `false` case it doesn't work for me the way I wanted - I still get a key-value pair: `property`: `null`. Is it still possible to do it somehow with a single JSX element? – Gal Grünfeld Feb 11 '19 at 19:52
  • 12
    I know this answer is quite old, so I do not know if things have changed in the interim, but for anyone stumbling across this answer in 2021: passing `undefined` as a prop is **not the same** as not setting the prop at all. Within a component, examining `Object.keys(props)` will reveal properties that have been set - even to the value of `undefined`. This matters for certain components which function as both controlled and uncontrolled components based on which props are set. – nicholas May 04 '21 at 20:38
103

Add a spread operator to the this.props.editable:

<Child {...(this.props.editable ? {editable: this.props.editableOpts} : {})} >

should work.

Amaresh S M
  • 2,936
  • 2
  • 13
  • 25
Jamal Hussain
  • 1,040
  • 1
  • 7
  • 3
  • 1
    Does this not yield the same result as editable= {this.props.editable ? this.props.editableOpts : undefined} Or is there a difference? – Flummiboy Jun 26 '19 at 09:47
  • 3
    @garyee: the difference is that some components might incorrectly treat `undefined` as an override (like `null`, which is always an override) so the only way to keep the default value instead of setting it to a falsy value is to explicitly not pass it, not pass `undefined`. – Yann Dìnendal Sep 16 '19 at 13:53
  • @YannDìnendal what if I use an empty object instead of undefined, like Does it make any difference? which one is better? – sktguha Aug 18 '20 at 18:31
  • 2
    @sktguha : yes indeed, looks like a typo I think: we can't spread undefined so yes, it should be a `{}` at the end. I'll suggest an edition, thanks :) – Yann Dìnendal Aug 20 '20 at 18:23
  • 2
    the expression can be simplified as `` – Normal May 19 '22 at 13:16
  • 2
    This seem like a much better answer for 2022 – Claudio Augusto Pereira Rolim Jul 08 '22 at 14:42
23

Define props variable:

let props = {};
if (this.props.editable){
  props.editable = this.props.editable;
}

And then use it in JSX:

<Child {...props} />

Here is a solution in your code:

var parent = React.createClass({
  propTypes: {
    editable: React.PropTypes.bool.isRequired,
    editableOpts: React.PropTypes.shape({...})
  },
  render: function() {
    let props = {};
    if (this.props.editable){
      props.editable = this.props.editable;
    }
    return (
      <Child {...props} />
    );
  }
});

Source, React documentation: https://facebook.github.io/react/docs/jsx-in-depth.html#spread-attributes

Leon Gilyadov
  • 670
  • 6
  • 13
20

Actually, if your prop is boolean it isn't needed to implement condition but if you wanna add prop by inline condition you should write like below:

const { editable, editableOpts } = this.props;
return (
  <Child {...(editable && { editable: editableOpts } )} />
);

Hope it doesn't confuse you. the {... means it is spread operator like passing existed props: {...props} and the editable && means if editable is true the { editable: editableOpts } object will make and with {... we will make a new object like it: {...{ editable: editableOpts }} that it means editable={editableOpts} but if this.porps.editable is true.

AmerllicA
  • 29,059
  • 15
  • 130
  • 154
  • I have been using this technique for a while now. Does this have any drawbacks? – aryankarim Apr 26 '22 at 07:24
  • @aryankarim, I'm still using this, not at all bad, it's awesome for when you don't want pass any key as props to the Child component. – AmerllicA Apr 26 '22 at 07:27
7

This may help you. I use this method often

    <Component
      otherProps
      {...(yourCondition ? { 
        deleteIcon: <DeleteIcon id={`${id}_icon`} />,
       }
      : {})}
    />

Another approach

<Component 
  {...(yourCondition && 
  { prop: propValue })}
/>

Junip Dewan
  • 759
  • 8
  • 8
2

also you can try this short hand way

 <Child {...(this.props.editable  && { editable: this.props.editableOpts })} />
mortezashojaei
  • 422
  • 2
  • 11
  • Wouldn't this cause a runtime error when `this.props.editable` is false because the spread operator would try to spread a boolean? – Martin Jan 05 '22 at 10:30
  • Nope- the syntax with logical && operator works because the transpiler (such as babel) evaluates the condition but does not expand when it encounters boolean false (as the spread operator only accepts iterables). If this doesn't work for you as expected, then you can always use the ternary operator syntax instead. Source: https://www.designcise.com/web/tutorial/how-to-conditionally-add-props-to-a-react-component – Joyce Lee Dec 09 '22 at 20:26
  • What the the parentheses mean before the spread operator? – Henry Dinh Dec 18 '22 at 08:45
0
var parent = React.createClass({
  propTypes: {
    editable: React.PropTypes.bool.isRequired,
    editableOpts: React.PropTypes.shape({...})
  },
  render: function() {
    return (
      <Child 
        {...(this.props.editable && {editable=this.props.editableOpts})} 
      />
    );
  }
});

This passes the props if they are defined. Else the props are not passed. In the other answer's the props are still passed but the value is undefined which still means the props are being passed.

sanair96
  • 39
  • 1
  • 7
0

Hey I might be late to jump in but I want to share a small tip. In case you are here for the reason that you want to pass props dynamically. Then you can use * sign to import all the stuff as an alias , i.e in this case as grs then the imports will be in an object, then you can use that object to pass props dynamically. Hope this will help some of you.

import * as grs from "../components/Theme";
import React from "react";
const GridInput = ({ sp, ...props }) => {
  return (
    <Grid {...grs[`gr${sp}`]}>
      <Input {...props} />
    </Grid>
  );
};
export default GridInput;
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
0

My take is to build an object with the wanted prop OR an empty object, and then spread it onto the child. Just 1 line of extra code before the jsx.


const EnhancedField = (props) => {

  // create an object with only the onChange prop defined if exists, or empty if not:
  const onChangeIfDefined = props.onChange ? { onChange: props.onChange } : {};
  
  return <Field
           {...onChangeIfDefined}
           name="..."
         />;

This way, if the prop is undefined or falsey, an empty object will be spread onto the child = onChange will not be passed. Both will work:

<EnhancedField name="vanilla" />
<EnhancedField name="enhanced" onChange={changeHandler} />
Arno Teigseth
  • 199
  • 2
  • 10