React: inline conditionally pass prop to component
Asked Answered
E

10

160

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().

Equi answered 26/8, 2015 at 17:10 Comment(2)
Can you include the code for Child as well? Also, did you mean to say <Child editableOpts={this.props.editableOpts} /> instead of <Child editable={this.props.editableOpts} />?Upi
@JimSkerritt I didn't confuse the props, though I know it looks that way. I'm trying to use react-bootstrap-table 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.Equi
U
222

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}
    />;
  }
});
Upi answered 26/8, 2015 at 17:32 Comment(6)
Oh, awesome! Did you find that in documentation somewhere? I was looking for that but couldn't find anything during a quick searchEqui
not sure if it's in the documentation or not, just something I've learned while using React :)Upi
null is not armed with such power as undefined, BTW.Ubangi
useful trick! wish there were good resources on how to effectively use conditional rendering techniquesPrepossess
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?Indecent
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.Essay
F
114

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

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

should work.

Favors answered 18/7, 2018 at 14:16 Comment(6)
Does this not yield the same result as editable= {this.props.editable ? this.props.editableOpts : undefined} Or is there a difference?Uund
@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.Lev
@YannDìnendal what if I use an empty object instead of undefined, like <Child {...(this.props.editable ? {editable: {this.props.editableOpts}} : {)} > Does it make any difference? which one is better?Ceratoid
@Ceratoid : 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 :)Lev
the expression can be simplified as <Child {...(this.props.editable && { editable: this.props.editableOpts })} />Khalsa
This seem like a much better answer for 2022Medium
E
24

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

Edson answered 5/8, 2017 at 17:33 Comment(1)
I like this solution. I'd use childProps to avoid confusion thoughWageworker
D
21

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.

Despotism answered 14/12, 2018 at 17:0 Comment(2)
I have been using this technique for a while now. Does this have any drawbacks?Tremendous
@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.Despotism
A
9

This may help you. I use this method often

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

Another approach

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

Allergic answered 16/6, 2022 at 14:25 Comment(0)
S
5

also you can try this short hand way

 <Child {...(this.props.editable  && { editable: this.props.editableOpts })} />
Song answered 15/8, 2020 at 11:24 Comment(3)
Wouldn't this cause a runtime error when this.props.editable is false because the spread operator would try to spread a boolean?Signorino
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: designcise.com/web/tutorial/…Juxtaposition
What the the parentheses mean before the spread operator?Thrush
C
1

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} />
Carpentaria answered 9/1, 2023 at 21:13 Comment(0)
D
1

a simpler yet efficient way using the double bars ||, javascript will send undefined in case the value is null

return (
        <Button size='sm' onClick={toggle}>
            <div className={`w-100 btn-light p-0`} >
                <div className="workiten-rel d-flex align-items-center">
                    <ProfileAvatar fs={8} user={selectedClient || undefined} />
                </div>
            </div>
        </Button>
)
Doggy answered 22/11, 2023 at 20:44 Comment(0)
B
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.

Blackandblue answered 10/10, 2019 at 9:39 Comment(5)
Passing undefined to a prop is equivalent to not passing it at all.Equi
@MatthewHerbst That is not true. Passing undefined is not the same as not passing at all.Mealworm
@Mealworm indeed it is. Try it.Equi
Oh man, I thought this was an Angular thread. Sorry.Mealworm
@MatthewHerbst My bad. I was under the illusion that passing undefined was equivalent to passing props.Blackandblue
A
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>
Apure answered 18/4, 2021 at 11:35 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.