I have onClick that I want to hit a certain function depending on the following logic:
onClick={
approve && this.handleApproveClick,
!approve && !releaseHold && this.handleDeclineClick,
releaseHold && this.handleReleaseHoldClick
}
Oddly enough the last this.handleReleaseHoldClick works, while the others do not. What is the correct way to do this? Or do I really need to create a separate button?
Why does only the last work?
It's a basic comma operator case, where the "comma operator evaluates each of its operands (from left to right) and returns the value of the last operand".
What is the correct way to do this?
Since it's a triple-condition function, I'd suggest you to create a class method and simply secure each of possible cases.
handleSomeClick = () => {
if (approve) {
this.handleApproveClick();
} else if (!approve && !releaseHold) {
this.handleDeclineClick();
} else if (releaseHold) {
this.handleReleaseHoldClick();
}
}
and inside JSX:
onClick={this.handleSomeClick}
Related
When working with javascript I always assumed that the double NOT operator (!!) behaves the same way as Boolean(), when trying to get a true / false out of a statement; however, since working with TypeScript I recently came across a scenario where they behaved differently, and I am trying to understand why that is. Take the following code for example:
import React from 'react'
interface Props {
myNumber: number | null
}
const Test: React.FC<Props> = ({ myNumber }) => {
return (
<>
{/* Example 1 */}
{!!myNumber && <span>{myNumber.toString()}</span>}
{/* Example 2 */}
{Boolean(myNumber) && <span>{myNumber.toString()}</span>}
</>
)
}
export default Test
Under example #1 the code works as I expect it: If the is rendered that means that myNumber passed the !! check, so it can never be null, thus myNumber.toString() will not return an error.
Under example #2 however, myNumber.toString() will return an error stating that "Object is possibly null", even though this code should only be reached if the Boolean(myNumber) returns true, and Boolean(null) should always be false.
Am I wrong thinking they should behave exactly the same? Is there something related to the TypeScrtipt compiler that I am missing? Thanks!
Boolean is typed as a function of type BooleanConstructor.
BooleanConstructor can be further broken down as <T>(value?: T | undefined) => boolean.
TypeScript does not consider the inner workings of a function when checking to see if a value may be null | undefined.
// Here TypeScript can directly infer that myNumber is indeed truthy
// by the preceeding double bang (!!) check.
const numberStrWithDoubleBang = !!myNumber && myNumber.toString();
// Here TypeScript cannot directly infer that myNumber is indeed truthy.
// To Typescript the function is a black box that takes in a number and
// returns a boolean, it does not know how the boolean value is calculated.
const numberStrWithFunction = isTruthy(myNumber) && myNumber.toString();
// While the isTruthy function above could easily have an implementation
// that does a simple truthy check on the passed in value as follows:
const isTruthy = <T>(value?: T | undefined) => !!value;
// It could also easily have an implementation with an output with no direct
// connection to the truthy status of the passed in value such as:
const isTruthy = <T>(value?: T | undefined) => {
const correctedValue = value ?? 0;
const midPoint = correctedValue / 2;
if (Math.random() * correctedValue > midPoint) {
return true;
}
return false;
};
At this point in time inferrence of null | undefined state via a function is considered to complex or the performance impact would be to great.
NOTE: !!0 and Boolean(0) are both false as 0 is a falsy value, if you need to get the string representation of zero you will need to update your checks.
I am trying to filter a List with multiple conditions. The main thing is, I must use the condition if it is true and not if it is false. If the condition is false I should not use that to filter. Below is my code
void performFiltering(bool homeVisits, bool onTheSpotServices)
{
//Check for home and on the spot filtering
if(homeVisits==true)
{
filteredOrganizationList = orgList.where((org) => org.homeVisits==true);
}
else if(onTheSpotServices==true)
{
filteredOrganizationList = orgList.where((org) => org.onTheSpotService==true);
}
else if(homeVisits==true && onTheSpotServices==true)
{
filteredOrganizationList = orgList.where((org) => (org.onTheSpotService==true) ||(org.homeVisits==true) );
}
}
here I have made simple if-else statements. Nothing serious. But I can't do this when there are more conditions. Luckily it is just 2 conditions, but I have much more to come.
Also carefully notice that I have used OR Command in the last statement. That means get results where either homeVisits=true or onTheSpotServices=true
Whats the most effective way of handing this?
there is no need for a cascade of multiple if-elses
instead use a single where with a custom test function:
filteredOrganizationList = orgList.where((org) =>
homeVisits && org.homeVisits ||
onTheSpotServices && org.onTheSpotService ||
... // rest of your filter tests
);
Its straightforward process;
Here is the origin render method I want it to be(I want my table outside of div):
but jsx compiler dont allow it for some reason?
but if i move the table inside of div element;
everything looks ok.
so only diff is place of table. why jsx interfere this process ? why its necessary ?
In JSX, we can return only one html element from return, can't return multiple elements, if you want to return multiple elements then wrap all the html code in a div or by any other wrapper component.
Same thing is happening in your 1st case, you are returning 2 elements, one div and one table. when you are wrapping them in one div everything is working properly.
Same rule you have to follow for conditional rendering of components.
Example:
Correct:
{ 1==1 /* some condition */ ?
<div>
True
</div>
:
<div>
False
</div>
}
Wrong:
{ 1==1 /* some condition */ ?
<div>
True 1
</div>
<div>
True 2
</div>
:
<div>
False 1
</div>
<div>
False 2
</div>
}
Just a quick update. If you are using React v16.2.0 and above you also can use Fragments.
return (
<>
<div>
True 1
</div>
<div>
True 2
</div>
</>
);
I also replied to a similar question, more in depth here
Another cause of this error is accidentally ending your line with a comma instead of a semicolon, this might happen because you make a mistake during refactoring of your code.
These give errors
return <div>hi</div>, // error
return (<div>hi</div>,) // error
These are correct:
return <div>hi</div>; // ok
return (<div>hi</div>) // ok
return (<div>hi</div>); // ok
This error is more cofusing in this case, since it highlights the whole JSX block, instead of the comma
This error occurs when the comma is interpreted as “comma operator” and not comma within arrays, objects, etc.
Comma operator evaluates each expression separated by comma and returns the last value.
const foo = (1, 2, 3) // returns 3
In this example, 3 will be assigned to foo. But it is highly likely that author thought (1, 2, 3) will be assigned like python tuple. So this error exists.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comma_Operator
So if you encountered this error, chances are there are some mistakes in your array/object/etc and it is interpreted as "comma operator".
Example code which causes this error.
const foo = (expr) => {
const bar = ""
const baz = ""
switch (expr) {
case 'Oranges', 'Mangoes': // error, you can't use comma for switch
break;
case 'Papayas':
break;
}
{ bar, baz } // You forgot to write return. Javascript thinks this is comma operator.
}
I'm trying to add a bit of logic to my controller with a couple of nested If statements. By themselves they work fine, but after nesting them, i'm not getting any results.
This is a working statement:
if (typeof object["Frequency"]!='undefined' && object["Frequency"]=='yearly' && ('now' <= 'upcoming')) {
$scope.summary[segment].totalLateRelationships++;
$scope.summary[segment].lateRelationships.push(object);
}
This is working:
if (!(object["nextmeetingowner"].length) || !(object["nextmeetingowner"].length) ) {
$scope.summary[segment].totalLateRelationships++;
$scope.summary[segment].lateRelationships.push(object);
}
This is what I'm trying to accomplish:
if (!(object["primaryaccountability"].length) || (!(object["nextmeetingowner"].length))) {
if (typeof object["Frequency"]!='undefined' && object["Frequency"]=='yearly' && ('now' <= 'upcoming'))
{
$scope.summary[segment].totalLateRelationships++;
$scope.summary[segment].lateRelationships.push(object);
}
}
That third code block is checking something different than the first two. It's evaluating
!(object["primaryaccountability"].length)
...whereas the earlier code is evaluating
!(object["Last Meeting Date"].length)
have the following function on my collection:
getFiltered: function (status, city) {
return this.filter(function (trainer) {
return ((status === null) ? trainer : trainer.get("TrainerStatusName") === status) &&
((city === null) ? trainer : trainer.get('City') === city);
});
}
What is is best way to deal with nullable params passed in i.e. if city it null then ignore filter/fetch all and if status is null then ignore filter/fetch all
The code above works, but curious about alternatives
Ok, first off, I'm a little confused by your question; the title says its about handling "nullable" parameters, but your code looks like it is dealing with "special case" parameters (specifically "all") ... except for the case of trainer being null, but I don't think that is even possible when iterating through a Backbone collection.
* * Edit * *
OP updated the question, so the above is no longer relevant. I've also updated my answer accordingly.
In any case, there's nothing at all wrong or unusual about your code; ternary operators are a standard way of handling one-off special cases. If you're looking for alternative ideas though, here's one that uses an extra function to DRY out (eliminate the duplication of) your code:
function matchesOrAll(expected, actual) {
return expected === null || expected === actual;
}
getFiltered: function (status, city) {
return this.filter(function (trainer) {
return matchesOrAll(status, trainer.get("TrainerStatusName") &&
matchesOrAll(city, trainer.get("City"));
}
* * Edit * *
Now that we're talking about null and not "all", it's worth pointing out that there is a better pattern for simpler cases of nulls/undefined. If you were just filtering cities, for instance, the code could just be:
getFiltered: function (expectedCity) {
return this.filter(function (currentCity) {
return expectedCity === (currentCity || expectedCity);
}
In other words, you could take advantage of Javascript's "truthiness", and the fact that disjunctive (ie. ||) booleans expressions return the first truthy value. This eliminates the need for a ternary, and many libraries use this pattern to fill in un-provided arguments; for instance, here's a line from jQuery that sets the "target" argument to a new object if none is provided:
target = arguments[1] || {};
Unfortunately though, when you're dealing with properties/attributes of things (eg. trainer.get('foo')) instead of just objects directly (eg. trainer), there's no good shortcut you can use (other than making a function).