How are boolean props used in React? - reactjs

I'm trying to clarify some confusion I have about boolean props in React.
Suppose a have MyComponent with several boolean props prop1, prop2...
First: it seems that boolean props are like just others: you can define default values, either in defaultProps or in destructuring params:
const MyComponent = ({ prop1, prop2 }) => (
...
);
MyComponent.defaultProps = {
prop1: false,
prop2: true,
}
Or (equivalently... I think)
const MyComponent = ({ prop1 = false, prop2 = true }) => (
...
)
What's not clear is how to pass values. The natural "React style", again, seems to be
<MyComponent prop1={ BOOLEAN_EXPRESION1 } prop2={ BOOLEAN_EXPRESION2 } />
... including the static literals (false/true).
However, it's also stated that the correct (recommended?) way to pass boolean properties is presence/absence of the attribute, as in HTML5.
So that, instead of <MyComponent prop1={true} prop2={false} />, one should write <MyComponent prop1 />.
My questions are:
What is the correct way of passing boolean props? Are both acceptable?
In case HTML5 style is the recommended (or correct) way, how would one deal with dynamic values?
In case HTML5 style is acceptable, what about default values?
In the example above (where prop2 is true by default), if I write <MyComponent />, what value would prop2 get?
Edited: To the accepted answer, I'd like to add my own tip: to play along nicely with the HTML5 style, design your boolean props so that they are by default false. Put in other way: a boolean prop that defaults to true should be considered an antipattern.

Forenote:
Let's just think this differently and disregard rules established by HTML5 and focusing only on JSX. JSX has exactly two ways of passing true, <MyComponent prop /> and <MyComponent prop={true} /> and exactly one way of passing false <MyComponent prop={false} />. I do agree this is an oddity distinguishing between HTML5 and JSX, but JSX is a syntax extension to JavaScript and not HTML so it does not need to conform to any of the rules of HTML.
From the JSX docs:
This funny tag syntax is neither a string nor HTML.
FYI, all rules and behavior of JSX is listed in React's JSX docs, and it includes how defaulting a prop to true works. Important note: these docs do not inherit anything from HTML and shouldn't be compared with HTML specs either.
Answers:
What is the correct way of passing boolean props?
Passing an explicit true in JSX:
There are exactly two ways to pass an explicit true: passing true and defaulting a prop to true:
<MyComponent prop={true} />
<MyComponent prop />
Note: As stated in the docs, JSX's behavior of defaulting a prop to true is just an added feature that matches with HTML5's boolean attributes behavior.
Passing an explicit false in JSX:
There is exactly one way to pass an explicit false: passing false
<MyComponent prop={false} />
Note: This is where JSX's behavior differ from HTML5's boolean attributes behavior. There is not such thing as defaulting to false in JSX; it is only applicable for passing an explicit true. In contrast to HTML5, if you do not pass anything, you're really passing undefined, and your defined default values will be used instead. This is contrary to the HTML5 specs which would say it's false. Refer to the CodeSandbox link in the answer to #3 for this behavior.
Passing a boolean variable/expression in JSX:
Pass the variable or an expression to the prop:
// via variable
const foo = true;
<MyComponent prop={foo} />
const bar = false;
<MyComponent prop={bar} />
// via expression
<MyComponent prop={Math.random() > 0.5} />
Are both acceptable?
Referring to the way of passing an explicit true vs defaulting a prop to true, they are both acceptable in terms of compiling JSX. However, if you need consistency in a codebase for adhering to a certain style, add the ESLint rule jsx-boolean-value. I personally use the Airbnb JavaScript style which turns that rule on. The guide emphasizes on readability, and so it decided for omitting the explicit true.
In case HTML5 style is the recommended (or correct) way , how would one deal with dynamic values?
Do not use HTML5 style (defaulting props to true) for dynamic values (variables/expressions); use the React way of passing props (explicitly assigning prop values). See above for how to pass those.
Furthermore, in case HTML5 style is acceptable, what about the default values? In the example above (where prop1,prop2 are resp. false/true by default) what would give?
Here are the final values for prop1 and prop2 passed respectively:
MyComponent.defaultProps = {
prop1: false,
prop2: true,
}
<MyComponent prop1 />
// prop1 == true
// prop2 == true // defaulted to true
<MyComponent prop1={true} prop2={false} />
<MyComponent prop1 prop2={false} />
// prop1 == true
// prop2 == false
Here is the CodeSandbox link: https://codesandbox.io/s/elated-davinci-izut1?fontsize=14&hidenavigation=1&theme=dark
Note: Not adding an attribute and then not passing a value (such as prop2 in the first example) is the same as passing undefined unlike passing an explicit false for the second example. Therefore, prop2 got defaulted to true.

Let me tell you about passing boolean values
HTML5 Style
<MyComponent
prop2 // equivalent prop2={true}, works only for static true values
prop1={false} // You can't do something like that for false though
/>
// example
<Select
multiSelect // You won't be changing this value throughout the whole life cycle
/>
// They will however transpiled to
React.createElement(MyComponent, {prop1: false, props2: true}, null)
// So passing true isn't necessarily a must
Default Props
This method isn't any different for boolean than other types
<MyComponent
title={title} // typeof title = string | undefined | null
bold={bold} // typeof bold = boolean | undefined | null
/>
// In case title being undefined or null,
// React will take the defaultProps
defaultProps are equivalent to
static defaultProps = {
title: 'Hello World',
bold: true
}
props = {
title: props.title ?? 'Hello World',
bold: props.bold ?? true
}
// Or in function, it's much simpler and familiar
// Same as old C, if the props are undefined or null,
// default values will be taken, this is plain 'ol JS
const MyComponent = ({title = 'Hello World', bold = true}) => {
// boop
}
So to summarize and answer your question
What is the correct way of passing boolean props? Are both acceptable?
Yes, both are acceptable. React is un-opinionated, It depends upon which style you're following, AirBnB recommends you to use HTML5 style for passing static true values, while old TypeScript screams at you to change it to props2={true}.
In case, HTML5 style is the recommended (or correct) way, how would one deal with dynamic values?
HTML5 is applicable only for static true values. There's simply no way for you to use dynamic boolean values in HTML5 style.
Furthermore, in case HTML5 style is acceptable, what about the default values? In the example above (where prop2 is true by default), if I write <MyComponent />, what would happen?
<MyComponent /> will be transpiled to React.createElement(MyComponent, {}, null), meaning prop1 and prop2 will be undefined. Given that the default values for prop1 is false while prop2 is true. Their respective default values will be taken.

Depends on use case. Ever heard of separation of concerns? The same principle apply here. Handle it where it makes sense to handle. You can use variables in a component instead of defaultProps (the React way). If its a switch pass it down from parent.

Related

How a prop of an element can be used for another prop in JSX?

I would like to use the value of a prop in another prop inside of the element, how can it be achived?
<CustomCard customName="foo" customProp={customName} />
You can't directly.
Something that may be helpful to understand is that JSX is just syntactic sugar for plain JavaScript.
<CustomCard customName="foo" customProp={customName} />
// Is equivalent too
React.createElement(CustomCard, {
customName: 'foo',
customProp: customName,
})
(which will not work of course).
You may define a local variable that you will pass to both props though:
const customName = "foo";
return <CustomCard customName={customName} customProp={customName} />

Destructuring when property name can vary

Learning destructuring and facing problem with this situation. I have this structure -
myObj{
key1: {prop1: true, prop2: false},
key2: {prop1: false, prop2: false}
}
MyObj can have multiple keys whose names are set dynamically but all those are having same set of properties, say prop1 and prop2. I want to destructure to get prop1 and prop2 without knowing real name of key1 or key2.
const { prop1, prop2 } = myObj[someVar]; // someVar can be key1 or key2
The only thing I can think of is this, if this is not what you want please comment, I will delete it
let myObj = {
key1: {prop1: true, prop2: false},
key2: {prop1: false, prop2: false}
}
Object.values(myObj).forEach((item)=>{
const {prop1, prop2} = item;
console.log('prop1: ', prop1 );
console.log('prop2: ', prop2 );
});
Answering my own question. I accepted kiranvj's solution but later realized following format is not wrong as well,
myObj[someVar].prop1; // or myObj[someVar].prop2;
// or
const { prop1, prop2 } = myObj[someVar];
Wrong thing I was doing was with inline if statement, later creating with prop1 and prop2. That was not giving correct result. And I thought I am not destructuring it right. Adding conditional statements as well. (Though not directly related to post but adding extra information for react-newbies like me.)
// this failed to give result
{ prop1 &&
((prop2 && <div>{allTrue}</div>))}
// simplified version gives correct result
{ prop1 && prop2 && <div>{allTrue}</div> }
It looks like the question has already been answered sufficiently, but I wanted to add some advice on this.
Unless you're working with a strongly typed version of JavaScript (e.g., Elm or TypeScript), I think it's a really bad idea to destructure an object's nested properties because it would produce type errors if the base property didn't exist beforehand (key1). This would potentially cause the app to fail.
If you're working in a strongly typed version of JavaScript, then the type enforcement would potentially error out at compile-time instead of run-time.
Single-layered destructuring is fine in my opinion though, because the worst that can happen is the assigned variable would become undefined.

React check if property exists AND is false

I have some form inputs and I'd like to be able to set some to be required and some to be optional. I'd like to use the required property so that the HTML is semantically correct:
<input onBlur={this.handleBlur} name="first_field" />
<input onBlur={this.handleBlur} name="first_field" required={false} />
<input onBlur={this.handleBlur} name="first_field" />
I'd prefer to add required={false} so I don't have to add the required prop to every field. However, checking for this then becomes somewhat strange:
handleBlur = (e) => {
if (e.target.value === '') {
// show an error if field empty and required
// NOT explicitly set to false
if (!this.props.required) {
setError('This field is required')
}
}
}
I can't do if (!this.props.required) because that would be true if the property isn't declared.
Is there a simpler way to handle this than the double check?
UPDATE: I hastily assumed this was a code issue, but after some discussion in comments below I now believe it is a semantic issue of how the HTML 'required' attribute should be used, OR how to handle this with React. I chose not to delete the question since there may be some value to this for others in future.
It sounds like you are trying to change the semantics of required. This attribute should default to false if it is not present. Explicitly setting required={false} is perfectly fine. However, changing the meaning when required is not present seems like a bad practice to me.
Instead, you can write your own component. You can name it OptionalInput for example. Give it a prop named optional and then have a render() function with something like this:
render() {
return <input name={this.props.name}
id={this.props.id}
...
required={!this.props.optional}/>
}
You will also need to add props for all input attributes which you want to support.
You can simplify it using defaultProps:
defaultProps: {
required: true
}
If the property is not defined, true will be used instead.
However, it might be better to invert the condition and call it optional, then you will be able to use it simply as:
<input name="first_field" optional />
The simplest way to approach this would be with a forced boolean casting. Any falsey values like undefined, null, 0, or false will result in false, any other value will be considered true. In this case, since required doesn't exist until it's specified as a prop, it will return false until assigned its value. You can try the following on your browser's terminal;
const data = {};
console.log('no required has been defined, ', !!data.required);
data.required = false;
console.log('required has been set to false, ', !!data.required);
data.required = true;
console.log('required should be true now, ', !!data.required);
delete data.required;
console.log('back to not defined, ' !!data.required);
Hope that helps!

What is the purpose of supports property in React

I am working with a legacy code and I am wondering what is the support property that is being used in most of the components. I haven't seen it yet in documentation, and can't find any info about it.
This is how the components look like with that property:
const Panel = connect(mapStateToProps, mapDispatchToProps)(injectIntl(handlingForm({
form: formName,
enableReinitialize: true,
})(PanelImpl)));
Panel.supports = (bp, apCodes) => bp === handlingCodes.PANEL|| actionPoints.some(ap => apCodes.includes(ap));
I have found that it is being used to check whether to render the component or not:
{Panel.supports(bp, apCodes)
&& (
<Panel
submitCallback={submitCallback}
readOnly={readOnly}
readOnlySubmitButton={readOnlySubmitButton}
apCodes={apCodes}
/>
)
}
So, I guess what I am wondering is, what is being returned from connect function, and does support property exists in the returned object or we can add any property to it, like supports is added here?

React: Set default proptype for array

In React I can set default PropTypes like so:
MyComponent.defaultProps = {
multiple: false
}
Now, one of my Props is an associative array. I want to set the default value for one of the key value pars, for example:
ImageMultiCheck.defaultProps = {
multiple: false,
myArray: {label: 'default Value'}
}
This is not working. Maybe its just a syntax mistake. How can I do that?
Default props is only used if no props are passed in for that specific property. If you are passing in a prop of myArray but the object has no key/value pair for label then you would have two options:
You could handle that case in the parent component and do a check to see if label exists on the object being passed down and if not add the default. Otherwise in your ImageMultiCheck component you would have to do some sort of var label = myArray.label ? myArray.label : 'default value'
Since it is a prop you don't want to mutate your myArray object by doing something like myArray.label = 'default value' in theImageMultiCheck` component hence why I suggested using a ternary assignment operation on a variable.

Resources