So I have a button thats suppose to change the backgroundcolor depending on a variable (props.status), which is an int.
I can understand that its possible to swap between two values e.g. using something like backgroundColor: props.status ? 'red' : 'blue', but what if I have many colors?
Kinda assumed something like this would work, but it doesn't.
backgroundColor: (() =>
{
switch (props.status)
{
case 0:
return 'red'
case 1:
return 'red'
default:
break;
}
})
You need to execute the function that you just declared:
(() => {
//...
})() // note the last pair of parentheses
This pattern is called IIFE
Related
When I have more than 2 options for conditional style in React, I'd rather not have ternary conditions within ternary conditions, so I use a self-invoking function like this:
const HeaderLink = ({
label,
linkTo,
firstItem = false,
lastItem = false,
}: {
label: string;
linkTo: string;
firstItem?: boolean;
lastItem?: boolean;
}) => {
return (
<div
id="HeaderLink"
style={{
padding: (() => {
if (firstItem) return "0 1vw 0 0";
if (lastItem) return "0 0 0 1vw";
return "0 1vw";
})(),
}}
>
<Link href={linkTo}>{label}</Link>
</div>
);
};
It works, but is there a smarter way? Are there reasons to avoid this pattern?
You may want to reuse your function
But even if you won't, even in that case you might want to have an easy time reading your code and define the function only once. Instead of a self-invoking function, you may create a function, like
function getWV(firstValue, secondValue) {
alert("I have been executed");
//...do your stuff
}
//proof-of-concept
getWV('a', 'b');
The example above shows that you can achieve a one-liner in your rendering code, only calling the function instead of defining it each time you render it and obscuring the readability of the renderer.
You can use helpful arrays
let array = ["0 1vw 0 0", "0 0 0 1vw", "0 1vw"];
function getItem(items)
{
let filteredArray = items.filter((item) => !!item);
if (filteredArray.length) return array[items.indexOf(filteredArray[0])];
return array[items.length];
}
console.log(getItem([null, 3]));
console.log(getItem([null, 0]));
At first sight this code may not appear much better than yours, but imagine the case when you may have more than two values. In that case you would have as many if conditionals as many items, while the approach above is much more dynamic.
I need to create a variable in JavaScript and assign it's value based on a condition. This works but feels a bit verbose:
const color = (() => {
switch (type) {
case "primary":
return CONSTANTS.colors.primary;
case "secondary":
return CONSTANTS.colors.secondary;
case "tertiary":
return CONSTANTS.colors.tertiary;
case "positive":
return CONSTANTS.colors.positive;
case "negative":
return CONSTANTS.colors.negative;
case "disabled":
return CONSTANTS.colors.disabled;
default:
throw new Error("A backgroundColor condition was missed");
}
})();
Is what I'm trying to do called "pattern matching"? Ive read that JavaScript doenst have this feature but Im not totally sure what it is.
Is there a more concise way of writing the code above? I could have lots of if statement but this feels messier and requires the variable to be let not const.
let color:
if (type === "primary") {
color = CONSTANTS.colors.primary;
} else if(type === "secondary") {
color = CONSTANTS.colors.secondary;
} else if(type === "tertiary") {
color = CONSTANTS.colors.tertiary;
} else if(type === "secondary") {
color = CONSTANTS.colors.secondary;
} else if(type === "positive") {
color = CONSTANTS.colors.positive;
} else if(type === "negative") {
color = CONSTANTS.colors.negative;
} else if(type === "disabled") {
color = CONSTANTS.colors.disabled;
}
The easiest solution for your problem is to check if the type is defined in the object CONSTANTS.colors. If you want to access a property by variable, you need to use the bracket annotation. Everything inside the brackets is evaluated as an expression (so type is a variable, 'type' the String value). Therefore, object.type returns the same value as object['type'].
let color = null;
if (typeof CONSTANTS.colors[type] !== 'undefined') {
color = CONSTANTS.colors[type];
} else {
throw new Error('A backgroundColor condition was missed');
}
console.log(color);
You can also first check if the key is defined in the object with Object.keys() and includes():
let color = null;
if (Object.keys(CONSTANTS.colors).includes(type)) {
color = CONSTANTS.colors[type];
} else {
throw new Error('A backgroundColor condition was missed');
}
console.log(color);
If you want to support IE11, you cannot use .includes(). Use .indexOf(type) !== -1 instead of .includes(type).
Pattern matching is generally referring to matching arguments passed to a function: testing to see if they match a specific "pattern". For example, a pattern match might allow you to write a function that takes an integer argument in "two different ways", one where the argument passed in is 0 and one when the argument passed is not 0 (the "otherwise" case). Switch statements are somewhat similar to this type of branching logic but aren't the same as a purely functional language like Haskell, and don't quite help with your goal here.
How about something like this instead?
const myColor = CONSTANTS["colors"][type];
if(typeof myColor !== 'undefined') {
color = myColor;
} else {
throw new Error("A backgroundColor condition was missed");
}
You are looking for property accessor:
color = CONSTANTS.colors[type];
An easy replacement for your code would be
const color = (() => {
const color = CONSTANTS.colors[type];
if (!color) {
throw new Error("A backgroundColor condition was missed");
}
return color;
}
})();
And no, that is not pattern matching.
I think it is wise to introduce an Enum that will hold the color values.
var ColorType = {
Primary: "primary",
Secondary: "secondary",
Tertiary: "tertiary,
...
};
Then you can use this enum in switch case and you will avoid the typos and referrence to string directly.
I think it will make the code less verbose and less prone to errors.
You can access a property of an object by using the property name as a string in square brackets.
(This example doesn't include the error catching you were using in your switch statement, but you can add that.)
const CONSTANTS = {
colors: {
primary: "blue",
secondary: "yellow"
}
}
function getColor(myPropName){
// Pass dynamic property names like this
return CONSTANTS.colors[myPropName];
}
console.log(getColor("secondary"));
I am trying to find in an array includes SOME of the text in an element. Here is what I have:
['red', 'green', 'blue'].some(e => e.includes('red:square')) // false
Which returns false. But I would like it to return true because obviously red is inside one of the elements of the array.
You can use Alternation ( | ) and Search function
console.log(['red', 'green', 'blue'].some(e => e.search(/red|square/)))
console.log(['red', 'green', 'blue'].some(e => ['red','square'].includes(e))) //positive test case
console.log(['red', 'green', 'blue'].some(e => ['white','square'].includes(e))) //negative test case
I am trying to make my switch work within JSX, but for some reason it doesn't work.
Every output has an ID, which is I. Now I am trying to make a switch statement with I, but it will always return the default value.
Why?
My code:
{(() => {
switch (i) {
case "0": return "{indents}";
case "1": return "{indents2}";
case "2": return "{indents3}";
default: return "{indents3}";
}
})()}
This is all within a div with attribute key={i}.
I would recommend maps instead. Maps/objects are type insensitive. It will also reduce execution complexity:
const map = {
0: indents,
1: indents2,
2: indents3
}
<div>{map[i] || indents3}</div>
Alternatively, since you are using 0,1,2.., you can also have it as an array.
const map = [indents, indents2, indents3];
<div>{map[i] || indents3}</div>
I have multiple conditions to check. I have to add icons based on the conditions, Then I need to change the background color based on some other set of conditions. I am using if statement. This is my code.
JSON:
{
"date": "2017-05-12",
"a": false,
"b": true,
"c": true,
"d": false,
"status": "active"
}
Javascript:
if (date != -1) {
//do something
if (a) {
//Add icon a
}
if (b) {
//Add icon b
}
if (c) {
//Add icon c
}
if (d) {
//Add icon d
}
}
if(status == "active"){
//Background Green
}
else if (status == "onhold"){
//Background Yellow
}
else if (status == "inactive"){
//Background Red
}
else{
//Backgeound Grey
}
How do I simplify it?
The first half of you code looks fine.
For the second half of your code you should make use of a switch statement. These replace the if-else statements you are using and decide what to do when certain "cases" occur. For example:
switch(status) {
case 'active':
//background green
break;
case 'onhold':
//background yellow
break;
case 'inactive':
//background red
break;
default:
//background grey
break;
}
My idea is:
var icons = {
a: 'a.png',
b: 'b.png',
c: 'c.png',
d: 'd.png',
}
if (date != -1) {
Object.keys(icons).forEach(function(key) {
if (data[key]) {
//Add icon icons[key]
}
});
}
var statusColors = {
active: 'Green',
onhold: 'Yellow',
inactive: 'Grey',
}
//Background statusColors[status]
I think it is pretty good as it is. Is is better to have understandable code than complex code that does exactly the same thing.
You don't have to do
if (a === true)
as it's equivalent to
if ( a )
There is no way to "simplify" it, but you can try to use switch statement instead:
switch (status) {
case 'active':
// active
break;
case 'onhold':
// onhold
break;
case 'inactive':
// inactive
break;
default:
console.log('default');
}
You can even "group" some conditions:
switch (status) {
case 'active':
case 'onhold':
// active AND onhold case
break;
case 'inactive':
// inactive
break;
default:
console.log('default');
}
More about switch statement -> https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Statements/switch
For Status variable you can use switch but for the first condition you have to use if-else statements I think.
switch (status) {
case "active":
//Background Green
break;
case "onhold":
//Background Yellow
break;
case "inactive":
//Background Red
break;
default:
//Backgeound Grey
}
a?setIconA():b?setIconB:c?setIconC;d?setIconD
and
status == "active" ? setGreen() : status == "onhold": setYellow()
and so on.
Your question doesn't quite give the details of the actions in each case, but if they're very similar, and there's a match between the property name and whatever action you need to take, you can use loops.
['a','b','c','d'].forEach(function (k)
{
if (objectFromJSON[k])
{
addIcon(k);
}
});
For the second part, it's slightly more complex as you have status names that don't match the color. You can either:
define CSS classes with those status names, and use the status name to set the class:
CSS:
.status-active
{
background: green;
}
.status-onhold
{
background: yellow;
}
.status-inactive
{
background: red;
}
JS:
theHTMLobject.classList.addClass('status-'+objectFromJSON.status);
use an object's properties (or a Map) to convert the status into a color
Do you mean "simplify" or do you mean "shorten" - because the two are almost mutually exclusive (shorter code is often not simpler!)
Your code is clear, and understandable. But it is a bit verbose, and can get much more complex as things grow. Sometimes it is better to shorten and the risk of making it a bit harder to understand.
You could consider things like a map between the status and the appropriate color
var backgroundStatusMap = {
"active":"green",
"onhold": "yellow",
"inactive": "red"
};
var backgroundColor = backgroundStatusMap[json.status];
Things like this can be added to easier if you as add new statuses - without having to trawl for the right place to put a new if.. condition.
Similarly, you could create a map for the booleans-to-icons
var iconMap = {
"a":"icon_a.png",
"b": "icon_b.png"
};
function getIcon(json, prop){
if(json[prop])
return iconMap[prop];
return null;
}
var iconA = getIcon(json,"a");
var iconB = getIcon(json,"b");