admin管理员组文章数量:1125925
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" : ""} />
}
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" : ""} />
}
Share
Improve this question
edited Dec 4, 2021 at 3:20
golopot
11.7k8 gold badges42 silver badges58 bronze badges
asked Jul 1, 2015 at 14:07
Remi StureRemi Sture
12.9k5 gold badges27 silver badges38 bronze badges
1
|
29 Answers
Reset to default 916Apparently, 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.
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' : null} />
);
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 null
.
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}
/>
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 })} />
);
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} />
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} />
);
}
}
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}} />
Using undefined
works for most properties:
const name = "someName";
return (
<input name={name ? name : undefined} />
);
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>
);
}
Given a local variable isRequired
You can do the following in your render method (if using a class) or return statement (if using a function component):
<MyComponent required={isRequired ? 'true' : undefined} />
In this case, the attribute will not be added if isRequired is undefined, false, or null (which is different from adding the attribute but setting it to 'false'
.) Also note that I am using strings instead of booleans in order to avoid a warning message from react (Boolean value received on non-boolean attribute).
- 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
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} >
Indeed, you may conditionally add an attribute depending on a certain circumstance by using a ternary expression. On the other hand, you must use caution while using the proper JSX syntax.
return <input className="foo" { ...(isRequired ? { required: true } : {}) } />
}
In this instance, the decision of whether or not to add the "required" property is made using a ternary expression. The "required" property will be applied to the element if isRequired is true. Nothing will be added in the event that isRequired is false. This is accomplished by inserting the attribute into JSX using the spread syntax (...).
As a result, properties will be added or removed based on the set criteria. In your scenario, the code will execute and add the "required" element if isRequired is true; otherwise, it will not add the attribute.
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>
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.
One way to do this is to use the spread operator (...) to pass a dynamic object of props to your component.
function MyInput({isRequired}) {
const inputProps = { className: 'foo' };
if (isRequired) {
inputProps.required = true;
}
return <input {...inputProps} />;
}
In this code, we first create an object inputProps with the className property. Then, if the isRequired prop is true, we add the required property to the inputProps object. Finally, we pass the inputProps object to the input component using the spread operator (...), which will dynamically add the className and required attributes to the input element.
Note that we're not adding the readOnly attribute in this example, but you could add it in a similar way by checking another prop and conditionally adding it to the inputProps object.
<input checked={true} type="checkbox" />
In react functional component you can try something like this to omit unnecessary tag property.
<div className="something" ref={someCondition ? dummyRef : null} />
This works for me if I need to omit tags like ref, class, etc. But I don't know if that's work for every tag property
<Button {...(isWeb3Enabled ? {} : { isExternal: true })}>
Metamask
</Button>
You must set as undefined the value for when you do not need the attribute Example:
<a data-tooltip={sidebarCollapsed?'Show details':undefined}></a>
The accepted answer did not work for me when using Apollo's MockedProvider component.
What worked:
const props = {
mocks: mockedApolloResponse ?? undefined, // simplified logic to make this example shorter
};
return <MockedProvider {...props}>{children}</MockedProvider>
Extra context:
I think the reason is that while mocks
is optional on MockedProviderProps
, it has to be of type ReadonlyArray<MockedResponse>
when present.
In my IDE, writing this line of code caused resulted in a warning:
<MockedProvider mocks={mockedApolloResponse ?? undefined}</MockedProvider>
The warning was: Type ReadonlyArray<MockedResponse> | undefined is not assignable to type ReadonlyArray<MockedResponse>
.
Use the conditional (ternary) operator from JavaScript within the JSX expression to conditionally add attributes to a React component based on a certain circumstance. However, in order to add the attribute dynamically, you must use the proper syntax.
Your example has a syntax error since you are attempting to add the "required" property via string interpolation. Instead, you must encapsulate the attribute and its value in curly brackets '{}'.
This modified version of your code appropriately adds the "required" property to the input component in a conditional manner and adds it to the input component:function MyInput({ isRequired }) { return <input className="foo" {isRequired ? 'required' : null} />; }
The "required" property is conditionally added in this code using the isRequired prop. The property is inserted with the value "required" if isRequired is true. Otherwise, the property is skipped if isRequired is false or null.
Keep in mind that classname, not class, is used to specify the element's CSS class.
If you need to conditionally add it depending on a different circumstance, be sure to handle the readOnly property in a manner that is comparable as well.
The answer by gian-marco-toso is technically correct in that the disabled
attribute will not be rendered in HTML, but if you're using a framework like material-ui, the actual MUI components will still render in a disabled state since they are just looking for the existence of the disabled
attribute on the JSX component rather than in the DOM :(
To get around this, I followed the advice from a comment by brigand.
Simply set the disabled
attribute to null
instead of false
.
export const MyInput = (props) => {
...
const disableField = useMemo<boolean>(() => {
// TODO : add complex logic here ...
return true;
}, []);
...
return (
<input
disabled={disableField ? true : null}
>
</input>
);
...
};
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}
/>
);
}
}
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/>
If it is for a limited number of properties this will do
function MyInput({isRequired}) {
if (isRequired) {
return <input classname="foo" isRequired={isRequired} />
}
return <input classname="foo" />
}
If you have a large number of properties, it will be difficult to write if else conditions for every property and return accordingly. For that, you can push those properties in an object and use the spread operator in the returned element.
function MyInput({ prop1, prop2, ...propN }) {
const props = {};
if (prop1) props.prop1 = prop1;
.
.
.
if (propN) props.propN = propN;
return <input classname="foo" {...props} />
}
you can following thiss code :
interface InputComponentProps {
required?: boolean;
}
const InputComponent = function(props:InputComponentProps ) {
const {required} = props
return (
<input
required={required === true ? true : false }
type="text"
{...props}
/>
);
}
本文标签: javascriptHow do I conditionally add attributes to React componentsStack Overflow
版权声明:本文标题:javascript - How do I conditionally add attributes to React components? - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1736666310a1946681.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
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. – Oleksiï Nikonov Commented Feb 13, 2019 at 9:46