React inline conditional component attribute - javascript

I've been searching everywhere and can't find an answer to my question. So I want a conditional attribute which is only displayed on certain conditions, example:
<Button {this.state.view === 'default' && 'active'}></Button>
As you can see I only want to indicate the button active if the this.state.view is equal to default. However, I get Unexpected token, error...
But when I try to put an attribute before it, for example:
<Button isActive={this.state.view === 'default' && 'active'}></Button>
It passes the syntax error and displays fine but this is not what I wanted to achieve.
How can I fix this? And what could be the reason behind it not passing?
UPDATE
So I just found out that in react-bootstrap the property active is a shorthand of active=true so I solved it using
<Button active={this.state.view === 'default'}></Button>
So In case, someone encounters this problem I'm leaving it here. However, I still want to know why the conditional attribute is failing without enclosing it inside a prop-like syntax, example:
This:
active={this.state.view === 'default'}
Versus
{this.state.view === 'default' && 'active'}

First of all, JSX is just a syntactic sugar for React.createElement. So, it may look like, but, in reality, you don't specify html attributes: in fact, you are always passing props.
For instance, the JSX code <input type="button" value="My button" /> is transpiled into React.createElement('input',{type:'button',value:'My Button'}). And, when necessary, React engine renders this React Element to the DOM as a HTML element.
That said, we have that JSX transpiles a prop without a value as true (check docs). For instance: <Button active></Button> is transpiled to React.createElement(Button, { active: true });.
But, we know that HTML5 specification does not accept attribute=true (as pointed here). For instance: <button disabled=true></button> is invalid. The correct is <button disabled></button>.
So, to render the HTML element to the DOM, React considers only props that are valid attributes (if not, the attribute is not rendered). Check all supported html attributes. And, then, finally, if it's a boolean attribute, it removes the true/false value, rendering it properly.
For instance: <button active={true}></button> is transpiled to React.createElement("button", { active: true }); and then React renders to the DOM <button></button>, because there is no active attribute in HTML specification for the <button/> tag (is not in the supported attributes list).
But <button disabled={true}></button> is transpiled to React.createElement("button", { disabled: true }); and React renders to the DOM <button disabled></button>.
I just said that to clarify your case.
You're trying to pass an active prop to the Button component (first letter uppercase means that's a React component: there is a React Component called Button handled somewhere in your code).
That means:
<Button active></Button> is transpiled to React.createElement(Button, { active: true });
and
<Button active={true}></Button> is transpiled to React.createElement(Button, { active: true });
The same thing!
So, if you want to do a conditional prop, you can simply do something like that:
<Button active={this.state.view === 'default'}></Button>
You have a condition inside brackets. Means that, if your this.state.view is equal to default (true), active prop will be passwed down to the component with the true value. If not equal, with the false value.
Button Component, then, must someway handle this active prop. It can render the button element and, for instance, change it's style, pass the disabled prop... I created a fiddle to demonstrate this: https://jsfiddle.net/mrlew/5rsx40gu/

Actually, I guess it is a duplicated question that I answered it in this link sometimes ago. for this specific post, there is no need condition prop for a boolean value, because it works well like below:
const { booleanValue } = this.props;
return (
<input checked={booleanValue} />
);
Or make your own boolean value:
const booleanValue = someThing === someOtherThing;
return (
<input checked={booleanValue} />
);
Both of them work well, because when the booleanValue is false, react doesn't see the active prop, hence it does not exist, unless you pass checked prop to a specific component that the component will receive false checked prop from this.props.
But, if you wanna a have a prop on your specific component with a condition and if the condition returns false you don't want it there are two ways, I like second:
First:
<Component someProp={condition ? yourValue : undefined} />
Second:
<Component {...(condition && { someProp: yourValue })} />

In JSX, component properties (or props) compile to a plain JavaScript object. Prop names are used as the keys of the object, and prop values are stored under those keys. The first example from the JSX docs does a great good job demonstrating this. In your case {view === 'default' && true} is a raw value without an associated prop name. Without a prop name to use as a key (specified by the syntax name=), JSX has nowhere to put that value in the final props object.
However, JSX will accept props via your original syntax if the expression inside the curly braces evaluates to an object. For example, you could do {{ active: view === "default" }}. In this case JSX can get both the key and value that it needs from the provided object, so no active= is necessary.

Related

Null values with OR condition inside jsx expression is not working

I have a piece of code that is supposed to render a component, or a placeholder div.
I've placed this inside the return block of another component, but I can't figure out why the placeholder div never renders.
Here is the piece of code in the return block mentioned:
{renderBannerSummary() || <div className={spacer} />}
renderBannerSummary is a function that renders a component that sometimes returns null.
I can see that null is indeed being returned on occasion via the console
I can also see that if I extract the logical OR statement entirely, and log it out, it logs the div whenever renderBannerSummary returns null, as expected:
So, my question is, why is it that the spacer div is never shown in the UI when renderBannerSummary returns null/is falsy?
Instead, nothing renders at all.
Any help appreciated, thanks
This should work for you:
{ renderBannerSummary() ? renderBannerSummary():<div className={spacer} /> }
This code will render <div className={spacer}/> if renderBannerSummary() is null, undefined or false
Apparently my conditional OR expression will never return the right hand side, as curly braces {} within the render part of a component will always trigger the code, meaning that my method call will always trigger, thus always being true - within the {}, my code was not conditional
There's nothing wrong with your or condition ||, but it all depends on your function that might return null
so you've the condition(inside jsx expression)
{ mightReturnNull() || <div>spacer</div> }
if mightReturnNull is written like this
function mightReturnNull(){
return null;
}
it will always render the spacer
but if mightReturnNull is written like this
function mightReturnNull(){
return <>{null}</>;
}
it basically returns a fragment element(or any wrapper element of your choice) and that's of course not a null and those it's not a falsy value, that's why you'll end up rendering the left-hand side, because it's a truthy value

Getting props from children — default values

EDIT: CodePen link here.
I'm (ab)using JSX to make a form-building 'DSL' for some of my non-technical colleagues. There's a SingleChoice component that can be used like this:
<SingleChoice>
<Option value="A">
// ... what to show when A is chosen
</Option>
<Option value="B">
// ... what to show when B is chosen
</Option>
</SingleChoice>
The result is a <div> full of radio button inputs, and under the block of inputs, there are the conditional elements (based on what option is chosen).
In other words, the Option elements don't render anything by themselves, they are there just to signal to the parent how many radio buttons there are, what are their labels and what should be shown when X is chosen. They are literally empty shells, made just to carry their props, like this:
function Option({label, value, children}) { return <></> }
Yes, I could instead pass those as an array of objects { value: string, show: ReactNode }, but that's not a friendly syntax for my non-dev colleagues.
Now, to the question. In the parent I go through all his children and render the input based on their value:
...
{children.map(ch => <> <input ... /> {ch.props.value} </>)}
...
The problem is, this only works when I manually pass a value prop to the Option component. E.g. when I have
function Yes({..., value = "Yes"}) { ... }
and I do
<SingleChoice>
<Yes />
</SingleChoice>
the label is empty, as if it didn't see the default prop value. Why is that happening? Is it a bug? And how do I implement this properly? Remember, I don't really want to expose any of the implementation details to the user who writes the form (so no explicit callback passing).
The only "proper" way I could think of would be creating a context with a callback in the parent, which all the children would look up and call with their values. The problem is, there would be a lot of contexts made and updated this way, and I fear the performance implications.
I'm a bit confused as to what you want to have happen. As far as I can tell from your code on Codepen, a default prop value is never defined so when you call
<Yes/>
without passing it a prop it doesn't get rendered by your parent component. Also, a react function should just accept props as a parameter, you don't need to define value and children.
I'm answering in another answer to get the formatting
The first error you have is you run ReactDOM.render in the beginning of your code. You should add it at the end of the code, or else default values won't be used(I don't know why this is).
The second is that your functions aren't really react. In react functions take one parameter, props. You can reference value or children as props.value or props.children.
not react: function Yes({value = "Yes1", children}) { return <span></span> }
react: function Yes(props){ return <span>?+{props.value}</span> }
The third is that declaring defaultProps is done through the defaultProps command in react. So instead of setting a parameter to a value(because again, no parameters) you should write:
Yes.defaultProps={ value:"Yes1" }
Finally, your function singleChoice isn't really react as well. Theres definitely a cleaner way to refactor your code then scrolling through the yes components and checking their value. However, you can do what you want. If you fix the other problems it should work as you intended.

Confusion using v-bind:class ternary on a v-for element when referencing the properties of the v-for element

I believe I've hit a stumbling block in my understanding of Vue. As far as I know, it's valid to reference the property of an object that is being listed using a v-for. However, when I try to do this inside of a v-bind:class ternary operation, it fails for some reason.
Context:
What I'm trying to do is basically have a component inside a v-for element emit an event, which the v-for element will pick up, and adjust it's own properties based on. In this case, I'm determining whether or not an order is due as a boolean value set via a reference to the v-for element (i.e. element as elements).
Example:
I've simplified the below code slightly, but have tested it with these versions and the problem remains.
Html:
<div class="order" :class="[order.due ? 'due' : '']" v-for="order in orders" v-cloak>
<div class="name" v-cloak>ORDER #{{order.id}}</div>
<time-count :date="order.submitted" v-on:due="due(order)"></time-count>
<div class="status" v-cloak>
{{order.status}}
</div>
</div>
and in JS, this method:
due(order) {
order.due = true;
},
and in the component:
if (this.minute >= 15 || this.hour > 0 || this.day > 0) {
this.$emit("due");
}
I have tested everything up to the point where I can see the order in the Vue object having the due property added, with the proper value associated with it (and the property doesn't exist before it's set). The ternary operator doesn't apply the class, even though I can see the the 'due' property being true for the specific order, like below:
{"order":"11","submitted":"2019-03-21 03:14:05","status":"CONFIRMED - AWAITING PAYMENT","due":true}
Properties that aren't available on the data object at the moment of the component creation are not reactive. You need to explicitly tell vue to add the new property.
Try this in your due method
due(order) {
this.$set(this.order, 'due', true)
}
Vue cannot detect property addition or deletion. Try having an initial value (like false) for due in the data section of your order component.

React: Return Statement

So I was reading on the Internet that
The vanilla JavaScript rule is, a return statement can only return one thing.
That one thing can be an array, an object, or React's >JSX case, a that contains multiple DOM elements.
Which Makes sense but then on the same place it was mentioned that
return [ ... cannot include a CSS class, style, an HTML attribute, ... but,
return <div classname="myClass" style={‌{color:"red"}} onClick={ ... }><p>A</p><p>B ...
can include a CSS class, style, an HTML attribute.
This statement is being little to confusing for me to understand.
"return [ ... cannot include a CSS class, style, an HTML attribute, ..."
[Question]: Can someone explain the above statement with example?
Also, this is a valid statement which we use in tutorial
return [
<p onClick={this.props.click}> Hey, I am {this.props.name}{this.props.children} and my age is {this.props.age} </p>,
<input type="text" onChange={this.props.changed} value={this.props.name} />
]
I guess we haven't used any html attribute above? but if we pass CSS class, or an HTML attribute such src or href, it won't work?
In JSX, return <someHtmlElement attribute='...'> is just a fancy syntax for a React.createElement('someHtmlElement... call, so essentially, you are still returning an object. Or, in case of your example return [ <p onClick...: an array of objects.
Also bear in mind that CSS class, style and HTML attributes only make sense in the context of an HTML element (simply put, between a < and a >), as those will all become part of the React.createElement call mentioned above. So this is why you can't directly return them in an array (i.e. return [ classname="myClass", style={‌{color:"red"}} ]): they don't have a meaning in "plain" JavaScript.
You can, however, return an array of HTML elements (which are essentially objects to JavaScript in this case), and those HTML elements of course can have CSS class, style and HTML attributes.
I hope this clears it up. When in doubt, just bear in mind that JSX simply ends up being JavaScript in the end, and try to think about what "vanilla" JavaScript would allow you to do.
what you see is 'jsx', a new syntax which came about when react was introduced. jsx looks like html, but gets converted to normal javascript function calls. You cannot use a file containing jsx and feed it to the browser. You will need some converter who converts the jsx code inside your file to javascript function calls. babel is the most famous converter of them all.
For e.g.
<div className='main' style={{backgroundColor: 'red'}}>abc</div>
gets converted to
React.createElement(
'div',
{ className: 'main', style: { backgroundColor: 'red' } },
'abc'
);
So, in your original question, what you are returning is not css properties or html, but whatever is returned by the function call React.createElement(). What does React.createElement return? It returns a plain javascript object. That object describes the html which has to be rendered. From your questions point of view, you are actually return an object.
And in your last example, therefore, you are returning an array of objects.
return [
<p onClick={this.props.click}> Hey, I am {this.props.name}{this.props.children} and my age is {this.props.age} </p>,
<input type="text" onChange={this.props.changed} value={this.props.name} />
]
P.S. You can check what javascript code your jsx will convert to here - https://babeljs.io/repl/
There is a strong difference between plain javascript syntax and React syntax. React use a syntax called JSX which thanks to compilers as Babel it is tanspiled JSX code in javascript.
To give you a better idea on what Babel does with JSX:
return [ <p />, <p className="hello" /> ];
it becomes:
return [React.createElement("p", null), React.createElement("p", { className: "hello" })];
You could visit https://babeljs.io/repl/ and see how transpiling works.

Do html5 data attributes need a value? [duplicate]

This question already has answers here:
Are empty HTML data attributes valid?
(4 answers)
Closed 7 years ago.
I am wondering if html data attributes actually need a value to be applied?
I wonder this because often all we want to know is if the attribute is actually set to act as a flag. (sure we could use a class for this; but realistically unless you are going to style these items differently then the flags are more data than a semantic item).
A perfect example of this is if we want a link to scroll to it's target instead of jumping our jQuery code might look like:
$(document).on('click', '[data-scroll-link'], function(){/**do scroll**/});
I know in google chrome it is sufficient for the anchor to appear as
<a href="#bottom" data-scroll-link>Scroll to bottom</a>
But will that work everywhere? and is it even valid HTML5 (I believe it is due to the autofocus, autoplay etc attributes). or do we need:
Scroll to bottom
No. But...
As is common with all attributes, in the application/xhtml+xml serialisation, XML rules apply and the attribute must have an explicit name and (quoted) value.
So this question is really about the text/html serialisation, and therefore the relevant part of the HTML5 spec is Section 8 The HTML syntax
In particular, under attributes, it says:
Attributes can be specified in four different ways:
where the first of these is:
Empty attribute syntax
Just the attribute name. The value is implicitly the empty string.
It's necessary to understand though that the value is of string type, not of boolean type.
For example, with <input id="cb" type="checkbox" checked>, the "checked" attribute is reflected by a property that is either true or false. So
if (document.getElementById("cb").checked)
will evaluate to true for the above markup.
In contrast, with <input id="cb" type="checkbox" data-checked>, the "data-checked" attribute is reflected via the dataset object as a string. The value of this property is the empty string, which in JavaScript is falsey. So,
if (document.getElementById("cb").dataset.checked)
will evaluate to false for the above markup.
To do the equivalent test, compare the value for "not undefined". I.e.
if (document.getElementById("cb").dataset.checked !== undefined)
will evaluate to true for the above markup.
See http://jsfiddle.net/GAxvW/
Simple Boolean Test For Element Attributes
To expand on Alohci's excellent answer, the following is a simple, flexible way to test for a true boolean attribute value supplied using one of three standard HTML conventions: <foo data-bar/>, <foo data-bar="true"/>, or <foo data-bar="data-bar"/>.
var a = elem['data-bar'];
var aTrue = ( a != null && a !== false && a !== 0 && a.charAt(0) != 'f' &&
a.charAt(0) != 'n' );
With the code above, the value is false if undefined or set to one of: f*, n*, 0 (case-insensitive), and true if defined and set to one of: (empty string), (attribute name), (anything else).
Empty strings are evaluated to true here because HTML attributes without values are '' which equal false in JS (and something like <foo disabled/> should equal <foo disabled="true"/>). You can use the above code for more general string testing by removing != null && a !== false.

Categories

Resources