Is there a way to only add attributes to a React component if a certain condition is met?
I’m supposed to add required and readOnly attributes to form elements based on an Ajax call after render, but I can’t see how to solve this since readOnly="false"
is not the same as omitting the attribute completely.
The example below should explain what I want, but it doesn’t work.
(Parse Error: Unexpected identifier)
function MyInput({isRequired}) {
return <input classname="foo" {isRequired ? "required" : ""} />
}
1
19 Answers
Apparently, for certain attributes, React is intelligent enough to omit the attribute if the value you pass to it is not truthy. For example:
const InputComponent = function() {
const required = true;
const disabled = false;
return (
<input type="text" disabled={disabled} required={required} />
);
}
will result in:
<input type="text" required>
Update: if anyone is curious as to how/why this happens, you can find details in ReactDOM’s source code, specifically at lines 30 and 167 of the DOMProperty.js file.
8
Generally
null
means “act like I didn’t specify it at all”. For boolean dom attributes true/false is preferred over repeating the attribute name/false, e.g.<input disabled>
compiles toReact.createElement('input', {disabled: true})
– Brigandreadonly
never gets added because React is expecting the attributereadOnly
(with a capital O).– MaxThanks! Make sure the value is not just an empty string or zero, these may not get removed. For example, you could pass a value like this, and it should make sure it is removed if it evaluates to false:
alt={props.alt || null}
.– JakeThanks, @Jake. I had tried setting the attribute to
false
, but onlynull
made sure the attribute was actually removed.
juandemarco’s answer is usually correct, but here is another option.
Build an object how you like:
var inputProps = {
value: 'foo',
onChange: this.handleChange
};
if (condition)
inputProps.disabled = true;
Render with spread, optionally passing other props also.
<input
value="this is overridden by inputProps"
{...inputProps}
onChange={overridesInputProps}
/>
4
This is actually very useful, especially when adding many properties conditionally (and I personally had no idea it could be done this way).
Very elegant, but shouldn’t it be inputProps.disabled = true?
– Joppevery simple, i have made my code more readable with out having multiple conditions.
If anyone cares about the precise semantics of this “sugar,” you can look at the script that your .jsx is transpiled into you’ll see that a function
_extends
has been added to it, which will (under normal circumstances) take theprops
constructed from the “normal attributes” and applyObject.assign(props, inputProps)
.
Here is an example of using Bootstrap‘s Button
via React-Bootstrap (version 0.32.4):
var condition = true;
return (
<Button {...(condition ? {bsStyle: 'success'} : {})} />
);
Depending on the condition, either {bsStyle: 'success'}
or {}
will be returned. The spread operator will then spread the properties of the returned object to Button
component. In the falsy case, since no properties exist on the returned object, nothing will be passed to the component.
An alternative way based on Andy Polhill’s comment:
var condition = true;
return (
<Button bsStyle={condition ? 'success' : undefined} />
);
The only small difference is that in the second example the inner component <Button/>
‘s props
object will have a key bsStyle
with a value of undefined
.
12
@Punit, The spread operator has a lower precedence than the conditional operator, so the condition is evaluated first, (either
{bsStyle: 'success'}
or{}
results from it), then that object is spread.Would the following do the same
<Button bsStyle={ condition ? 'success': undefined } />
I find the syntax slightly easier, passingundefined
will omit the property.@AndyPolhill looks good to me and much easier to read the code, the only small difference is that in your code example inner component
<Button/>
‘sprops
object will have a keybsStyle
with value ofundefined
.The first method worked perfectly for me, thank you
– Liran HVery simple and easy way <3
– user13353216
Here is an alternative.
var condition = true;
var props = {
value: 'foo',
...( condition && { disabled: true } )
};
var component = <div { ...props } />;
Or its inline version
var condition = true;
var component = (
<div
value="foo"
{ ...( condition && { disabled: true } ) } />
);
4
I like this approach, it makes me cool among my workmates. Kidding aside, from the looks of it, the props are just passed as a key-value pair after all, is that correct?
– JohnnyQIf
condition
is false, this will try to expand/iterate over false, which I don’t think is correct.@LarsNyström, That makes sense. The spread operator accepts only iterables, where
false
is not one. Just check with Babel. This works with it whencondition
evaluates to false since the way Babel implements the operator. Though a trivial workaround could be...( condition ? { disabled: true } : {} )
, it becomes a bit verbose then. Thanks for this nice input!– Season+1 This approach is required if you want to conditionally output
data-*
oraria-*
attributes, as they’re a special case in JSX, sodata-my-conditional-attr={ false }
will outputdata-my-conditional-attr="false"
rather than omitting the attribute. facebook.github.io/react/docs/dom-elements.html– ptim
Here’s a way I do it.
With a conditional:
<Label
{...{
text: label,
type,
...(tooltip && { tooltip }),
isRequired: required
}}
/>
I still prefer using the regular way of passing props down, because it is more readable (in my opinion) in the case of not have any conditionals.
Without a conditional:
<Label text={label} type={type} tooltip={tooltip} isRequired={required} />
2
Could you pls explain how this part works –
...(tooltip && { tooltip }),
? It does work on component but when I try to use something like this in the code I get an error meaning that I try to spread non-iterable valueprobably because
falseyValue && {}
will return false, so its likely you are spreading on false eg...(false)
. much better to use full expression so the spread continues to behave...(condition ? {foo: 'bar'} : {})
Let’s say we want to add a custom property (using aria-* or data-*) if a condition is true:
{...this.props.isTrue && {'aria-name' : 'something here'}}
Let’s say we want to add a style property if a condition is true:
{...this.props.isTrue && {style : {color: 'red'}}}
You can use the same shortcut, which is used to add/remove (parts of) components ({isVisible && <SomeComponent />}
).
class MyComponent extends React.Component {
render() {
return (
<div someAttribute={someCondition && someValue} />
);
}
}
2
If
someCondition
is true butsomeValue
is falsy (e.g.false
itself, or0
, etc.), does the attribute still get included? This is important if it is necessary to explicitly include a falsy value, e.g. a0
for a coordinate attribute, etc.Normally, the attribute is omitted, but not in the case of
data-*
andaria-*
, see my comment above. If you quote the value, or cast it as a String, the attribute will display: egsomeAttr={ `${falsyValue}` }
could rendersomeAttr="false"
– ptim
If you use ECMAScript 6, you can simply write like this.
// First, create a wrap object.
const wrap = {
[variableName]: true
}
// Then, use it
<SomeComponent {...{wrap}} />
2
where I can find documntation about this syntax?
This should work, since your state will change after the Ajax call, and the parent component will re-render.
render : function () {
var item;
if (this.state.isRequired) {
item = <MyOwnInput attribute={'whatever'} />
} else {
item = <MyOwnInput />
}
return (
<div>
{item}
</div>
);
}
0
Using undefined
works for most properties:
const name = "someName";
return (
<input name={name ? name : undefined} />
);
For example using property styles for custom container
const DriverSelector = props => {
const Container = props.container;
const otherProps = {
...( props.containerStyles && { style: props.containerStyles } )
};
return (
<Container {...otherProps} >
In React you can conditionally render Components, but also their attributes, like props, className, id, and more.
In React it’s very good practice to use the ternary operator which can help you conditionally render Components.
An example also shows how to conditionally render Component and its style attribute.
Here is a simple example:
class App extends React.Component {
state = {
isTrue: true
};
render() {
return (
<div>
{this.state.isTrue ? (
<button style={{ color: this.state.isTrue ? "red" : "blue" }}>
I am rendered if TRUE
</button>
) : (
<button>I am rendered if FALSE</button>
)}
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
2
This can get really messy with lots of attributes. I like the spread variant better.
–Yes Your are right but this is for someone who need to get overview I will make another example. But want keep things simple.
– Juraj
From my point of view the best way to manage multiple conditional props is the props object approach from @brigand. But it can be improved in order to avoid adding one if
block for each conditional prop.
The ifVal helper
rename it as you like (iv, condVal, cv, _, …)
You can define a helper function to return a value, or another, if a condition is met:
// components-helpers.js
export const ifVal = (cond, trueValue=true, falseValue=null) => {
return cond ? trueValue : falseValue
}
If cond
is true
(or truthy), the trueValue
is returned – or true
.
If cond
is false
(or falsy), the falseValue
is returned – or null
.
These defaults (true
and null
) are, usually the right values to allow a prop to be passed or not to a React component. You can think to this function as an “improved React ternary operator”. Please improve it if you need more control over the returned values.
Let’s use it with many props.
Build the (complex) props object
// your-code.js
import { ifVal } from './components-helpers.js'
// BE SURE to replace all true/false with a real condition in you code
// this is just an example
const inputProps = {
value: 'foo',
enabled: ifVal(true), // true
noProp: ifVal(false), // null - ignored by React
aProp: ifVal(true, 'my value'), // 'my value'
bProp: ifVal(false, 'the true text', 'the false text') // 'my false value',
onAction: ifVal(isGuest, handleGuest, handleUser) // it depends on isGuest value
};
<MyComponent {...inputProps} />
This approach is something similar to the popular way to conditionally manage classes using the classnames utility, but adapted to props.
Why you should use this approach
You’ll have a clean and readable syntax, even with many conditional props: every new prop just add a line of code inside the object declaration.
In this way you replace the syntax noise of repeated operators (...
, &&
, ? :
, …), that can be very annoying when you have many props, with a plain function call.
Our top priority, as developers, is to write the most obvious code that solve a problem.
Too many times we solve problems for our ego, adding complexity where it’s not required.
Our code should be straightforward, for us today, for us tomorrow and for our mates.
just because we can do something doesn’t mean we should
I hope this late reply will help.
1
Very neat! I am surprised why this answer has no upvotes!
– Sisir
Considering the post JSX In Depth, you can solve your problem this way:
if (isRequired) {
return (
<MyOwnInput name="test" required='required' />
);
}
return (
<MyOwnInput name="test" />
);
I think this may be useful for those who would like attribute’s value to be a function:
import { RNCamera } from 'react-native-camera';
[...]
export default class MyView extends React.Component {
_myFunction = (myObject) => {
console.log(myObject.type); //
}
render() {
var scannerProps = Platform.OS === 'ios' ?
{
onBarCodeRead : this._myFunction
}
:
{
// here you can add attribute(s) for other platforms
}
return (
// it is just a part of code for MyView's layout
<RNCamera
ref={ref => { this.camera = ref; }}
style={{ flex: 1, justifyContent: 'flex-end', alignItems: 'center', }}
type={RNCamera.Constants.Type.back}
flashMode={RNCamera.Constants.FlashMode.on}
{...scannerProps}
/>
);
}
}
- For some boolean attributes listed by React [1]:
<input disabled={disabled} />
// renders either `<input>` or `<input disabled>`
- For other attributes:
<div aria-selected= {selected ? "" : undefined} />
// renders either `<div aria-selected></div>` or `<div></div>`
[1] The list of boolean attributes: https://github.com/facebook/react/blob/3f9480f0f5ceb5a32a3751066f0b8e9eae5f1b10/packages/react-dom/src/shared/DOMProperty.js#L318-L345
in an easy way
const InputText= ({required = false , disabled = false, ...props}) =>
(<input type="text" disabled={disabled} required={required} {...props} />);
and use it just like this
<InputText required disabled/>
In React, we pass values to component from parent to child as Props. If the value is false, it will not pass it as props. Also in some situation we can use ternary (conditional operator) also.
May be one comment help someone, i found out React 16.7 doesnt rerenders and update the component’s html attributes if you changed only them in a store (f.e. redux) and tied to component. This means the component has f.e.
aria-modal=true
, you push the changes (to false) to the store of aria/data attributes, but nothing else is changed (such as component’s content or class or variables in there) as the result ReactJs will not update aria/data attrs in that components. I’ve been messing around about whole day to realise that.