Conditionals in React - javascript

How do I use conditionals in React?
Essentially, for customerClassesRemaining, I currently say "class(es)" but I want to write code that says if there's one class, use "class," and if there's more than one class, use "classes." Thanks!
render() {
const loadBoxes = (
Array.from({ length: 20 }).map((n, i) => (
<LoadBox key={`${i}${n - 1}key`} />
))
);
const customerBoxes = !this.props.customersLessThanFive ? loadBoxes
: this.props.customersLessThanFive.map((customer, i) => (
if (customer.classes_remaining == 1) {
customerClassesRemaining2 = {`${customer.classes_remaining} class left`}
}
else {
customerClassesRemaining2 = {`${customer.classes_remaining} classes left`}
}
<CustomerFewerFiveBox
key={customer.id}
customerIndex={i + 1}
customerName={`${customer.firstName} ${customer.lastName}`}
customerClassesRemaining={`${customer.classes_remaining} class(es) left`}
customerPackage = {customer.name}
customerExpiration = {`Expiration: ${customer.expiration_date}`}
/>
))

you can try,
customerClassesRemaining2 = customer.classes_remaining == 1 ? `${customer.classes_remaining} class left` : `${customer.classes_remaining} classes left`;

Using ternary operators like:
customerClassesRemaining={`${customer.classes_remaining} class${customer.classes_remaining > 1 ? 'es' : ''} left`}

Related

Adding parent div's inside .map

Is there any way I can include the wrapping div's FilterListContainer, FilterListScroll and FilterList in the map itself?
So if there is something to map, it will add the parent div's. If not it wont.
<FilterListContainer>
<FilterListScroll>
<FilterList>
{Object.keys(this.props.body_search_filter)
.map((k) => (
<SidebarFilter
key={k}
type={k}
filter={this.props.body_search_filter[k]}
handleChange={this.handleFilterChange}
/>
))
.filter(
(i) =>
i.props.filter.list.length > 0 &&
((!i.props.filter.optional && !i.props.filter.hidden) ||
(i.props.filter.list.length !== 1 &&
!i.props.filter.list[0].disabled))
</FilterList>
</FilterListScroll>
</FilterListContainer>
You'll be able to use short-circuiting of logical operators here:
{Object.keys(this.props.body_search_filter).length && (
<FilterListContainer>
<FilterListScroll>
<FilterList>
{Object.keys(this.props.body_search_filter)
.map((k) => (
<SidebarFilter
key={k}
type={k}
filter={this.props.body_search_filter[k]}
handleChange={this.handleFilterChange}
/>
))
.filter(
(i) =>
i.props.filter.list.length > 0 &&
((!i.props.filter.optional && !i.props.filter.hidden) ||
(i.props.filter.list.length !== 1 &&
!i.props.filter.list[0].disabled))
</FilterList>
</FilterListScroll>
</FilterListContainer>
)}
But you might want to filter the list, then check if the filtered list has any elements instead:
const filtered = Object.keys(this.props.body_search_filter).filter((k) => {
const f = this.props.body_search_filter[k];
return f.list.length > 0 &&
((!f.optional && !f.hidden) ||
(f.list.length !== 1 && !f.list[0].disabled))
});
// ...
// then use 'filtered' instead
{filtered.length && (
<FilterListContainer>
<FilterListScroll>
<FilterList>
{filtered.map((k) => (
<SidebarFilter
key={k}
type={k}
filter={this.props.body_search_filter[k]}
handleChange={this.handleFilterChange}
/>
))}
</FilterList>
</FilterListScroll>
</FilterListContainer>
)}
Moving my comment to an answer to add a snippet
You could do the map before the return. Then you can 'check' if the map has some content, if so, use it, otherwise, don't render (or use fallback)
Please see comments in code
const { useState } = React;
const Example = () => {
// Demo data
const [data, setData] = useState([ 'foo', 'bar' ])
// Filter + Mapping logic
const mapped = data
.filter(d => d !== 'foobar')
.map(d => <li>{d.toUpperCase()}</li>);
// Content based on map, use it, or render 'null'
// The wrapped 'ul' is OP's FilterListContainer as an example
const content = !mapped.length ? null :
(
<div>
<ul>
{mapped}
</ul>
</div>
);
// Render
return content;
}
ReactDOM.render(<Example />, document.getElementById("react"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="react"></div>

How to add a part of code to the ternary operator using javascript and react?

i have code like below,
const variableType = 'INTEGER';
const editedValue = 100;
const defaultValue = 4;
return (
isOpen ? (
<span>something</span>
) : (
<>
{(variableType ?? '').toUpperCase() === 'BOOLEAN'
? capitalize(editedValue ?? '')
: {editedValue}
</>
);
Now i want to add is when this variableType is not boolean and editedValue is not same as defaultValue i want to show an icon along with editedValue
if variableType is not boolean and edited Value is same as defaultValue i want to just show editedValue.
i have tried something like so,
return (
isOpen ? (
<span>something</span>
) : (
<>
{(variableType ?? '').toUpperCase() === 'BOOLEAN'
? capitalize(editedValue ?? '')
: {editedValue !== defaultValue && (
<Icon />
}
{editedValue}
</>
);
But the above doesnt seem right syntatically.
how can i change the above ternary operator to satisfy above condition and show icon. could someone help me with this. i am new to using ternary operator. thanks.
The key here is understanding what parts are in JSX elements and what parts aren't. When you're in an element or fragment, you're in a JSX element and you use {...} to insert values or code. When you're inside {...}, you're not in a JSX expression.
I think you probably want:
return (
isOpen ? (
<span>something</span>
) : (
(variableType ?? '').toUpperCase() === 'BOOLEAN'
? capitalize(editedValue ?? '')
: <>
{editedValue !== defaultValue && <Icon />}
{editedValue}
</>
);
....but I would suggest you break that up to make it easier to read and maintain. For instance:
if (isOpen) {
return <span>something</span>;
}
if ((variableType ?? '').toUpperCase() === 'BOOLEAN') {
return capitalize(editedValue ?? '');
}
return <>
{editedValue !== defaultValue && <Icon />}
{editedValue}
</>;
if (isOpen) return <span>something</span>
return (
<>
{(variableType ?? '').toUpperCase() === 'BOOLEAN'
? capitalize(editedValue ?? '')
: <>
{editedValue !== defaultValue && <Icon />}
{editedValue}
</>
}
</>
)

Toggle 0 and 1 as boolean values in React

I'm making a toggle component in React and send the state to my database using LARAVEL.
When I'm sending the state of the toggle, in the database it's registered as 1 or 0, but when I'm activating/disabling the toggle, in the console.log it shows the values of true & false.
The problem here is that when I send the value to the database and I want to get it, the toggle doesn't recognize 0 and 1 because I'm writing a condition for if true or false, so I can't read the values registered on the db which are 0/1.
Is there a solution on how can I convert the received boolean to true/false ?
The hook and Handler :
const [showlogo, setshowlogo] = useState('');
const HideLogo = () => {
setshowlogo(!showlogo);
console.log(showlogo)
}
The toggle code :
<div onClick={HideLogo} >Toggle</div>
<div className={`${ showlogo ? "on" : "off" }`}>Valeur of logo</div>
If you want to toggle 0 and 1 as boolean values:
const { useState } = React
const response = {data: {showlogo: 0}}
function App() {
const [showlogo, setShowlogo] = useState(response.data.showlogo);
const hideLogo = () => {
setShowlogo(prev => prev === 0 ? 1 : 0); // <-- HERE
}
return (<div>
<div onClick={hideLogo} >Toggle (click), showlogo: {showlogo}</div>
<div className={showlogo===1?"on":"off"}> Valeur of logo</div>
</div>)
}
ReactDOM.render(<App />, document.body)
<script crossorigin src="https://unpkg.com/react#17/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#17/umd/react-dom.production.min.js"></script>
Other answers recommended using Boolean, but since your db returns 1 or 0, and sounds like you have no control over it, then alternative use === operator
showLogo === 1 ? 'on' : 'off'
and use the following to toggle between 1 and 0
setShowLogo( showLogo === 1 ? 0 : 1 )
const status = 0
console.log(Boolean(status === 0))
console.log(Boolean(status === 1))
// IF it's a string then use String
const stringStatus = '0'
console.log(Boolean(stringStatus === '0'))
console.log(Boolean(stringStatus === '1'))
if you are dealing with 1 and 0 coming from the database, you can cast them to boolean with Boolean(x) -
Boolean(0) // false
Boolean(1) // true
and then if you have to save them again as 0 or 1, convert them back to a number from a boolean -
const x = showLogo ? 1 : 0
// then send x back to the db
Your initial value should be boolean
import React from "react";
let count = 1;
export default function App() {
const [showlogo, setshowlogo] = React.useState(0);
const HideLogo = (id) => {
if (id % 2 !== 0) setshowlogo(1);
else setshowlogo(0);
count++;
};
return (
<div>
<button onClick={() => HideLogo(count)}>Toggle</button>
{showlogo && (
<div className={`${showlogo ? "on" : "off"}`}>Valeur of logo</div>
)}
</div>
);
}
Demo
Just pass your id received from DB
onClick={() => HideLogo(ID)}
and toggle here based on passed ID
const HideLogo = (id) => {
if (id=== 0) setshowlogo(1);
else setshowlogo(0);
};

How to render conditional jsx using ternary operator in react?

i want to use ternary operator for conditional rendering of jsx using react and javascript.
below is my code,
const Parent = () => {
return (
<Child
isChecked={true}
isMany={true}
/>
);
};
const Child = (props) => {
const name=defaultTo(props.optionName, 'name');
return (
{props.isMany
? `${props.getValue().length} ${name}${
props.getValue().length > 1 ? 's' : ''
} selected`
: props.getValue()[0].label
}
);
}
the above code works and it returns output like "2 names selected" or "1 name selected" based on getValue length. here Child component is reusable and isChecked prop is optional. it may or maynot be passed as prop to this Child component.
Now if the isChecked prop is true, i want to display output like "2 selected" or "1 selected" which depends on getValue length.
how can i modify the above code to do so.
could someone help me with this. thanks.
EDIT:
below is what i have tried.
const Child = (props) => {
const name= props.isChecked ?
defaultTo(props.optionName, '')
: defaultTo(props.optionName, 'name');
return (
{props.isMany
? `${props.getValue().length} ${name}${
(props.getValue().length > 1 && !isChecked) ? 's' : ''
} selected`
: props.getValue()[0].label
}
);
}
this kind of works but still displays that 's' if more than one count so the output is like below
1 selected
2 s selected
3 s selected
....
I'd do much of the work before the return, something along these lines:
const Child = (props) => {
const {isChecked, isMany, optionName} = props;
const value = props.getValue();
const {length} = value;
const nameDisplay = isMany && `${defaultTo(optionName, "name")}${length === 1 ? "" : "s"}`;
return (
isMany
? isChecked
? `${length} selected`
: `${length} ${nameDisplay}`
: value[0].label;
);
};
I should note that just adding an s works for name, but doesn't work for many other nouns in English (bus for instance, or mouse). Making plurals in English is, sadly, fairly complicated in practice. Rather than accepting just optionName, you might consider accepting something that includes both the singular and the plural.
Side note: ? : is a ternary operator (an operator accepting three operands, like a binary operator accepts two operands and a unary operator accepts one), and it's JavaScript's only ternary operator at the moment, but that could change. Its proper name is the conditional operator.
You can make another ternary operator.
const Child = (props) => {
const name=defaultTo(props.optionName, 'name');
return (
{props.isChecked ?
props.getValue().length + " selected" :
(props.isMany
? `${props.getValue().length} ${name}${
props.getValue().length > 1 ? 's' : ''
} selected`
: props.getValue()[0].label)
}
);
}

Matching multiple substrings to a string

I have an application where the user types in text, then this text is sent to the server and it returns an array of words that contain this text.
But as you can see the problem starts when there are more then 1 match. Here's my current code:
state.input !== '' && vocabularyItems && (vocabularyItems.map((vocabularyItem, index) => {
const regex = new RegExp(input, 'gi');
const results = vocabularyItem.matchAll(regex);
const tmp = [];
console.log(vocabularyItem);
for (const match of results) {
console.log(match);
let currentLoop = vocabularyItem.slice(0, match.index);
currentLoop += '<strong className="tt-highlight">';
currentLoop += vocabularyItem.slice(match.index, match.index + input.length);
currentLoop += '</strong>';
currentLoop += vocabularyItem.slice(match.index + input.length, vocabularyItem.length);
tmp.push(currentLoop);
}
console.table(tmp);
return (
<div
id={index}
className={`override-strong tt-suggestion tt-selectable ${cursor === index && 'tt-cursor'}`}
onMouseDown={handleClick}
key={index}
dangerouslySetInnerHTML={{ __html: tmp }}
/>
);
}))
and here are some examples in HTML code
1.
<strong className="tt-highlight">En</strong>kelkind
2.
<strong className="tt-highlight">En</strong>gagement
Engagem<strong className="tt-highlight">en</strong>t
as you can see, it works when there is only one occurence, but duplicates the word when more then one match is present. How can I end up with just something like
<strong>en</strong>gagem<strong>en</strong>t?
engagement?
I forgot to add that I need the case preserved
First off, I would recommend to use, something, like:
const results = vocabularyItems.filter(word => word.toLowerCase().includes(input.toLowerCase()))
for case insensitive vocabulary lookup.
Next, I would highlight the match in a bit different way. I'd split suggested option into parts (matching search input and non-matching ones) , then style those respectively:
const parts = suggestion.split(new RegExp(`(?=${match})|(?<=${match})`, 'gi'))
...
parts.map((part,key) => {
const style = part.toLowerCase() == match.toLowerCase() ? 'bold' : 'normal'
return <span style={{fontWeight:style}} {...{key}}>{part}</span>
})
I think it's safe enough to assume that you build autocomplete search input, thus you might find of use the quick demo below (excluding all the styling):
//dependencies
const { render } = ReactDOM,
{ useState } = React
//vocabulary
const vocabulary = ['engagement', 'Bentley', 'English', 'seven', 'Engagement']
//suggested option component
const SuggestedOption = ({suggestion, match}) => {
const parts = suggestion.split(new RegExp(`(?=${match})|(?<=${match})`, 'gi'))
return (
<div>
{
parts.map((part,key) => {
const style = part.toLowerCase() == match.toLowerCase() ? 'bold' : 'normal'
return <span style={{fontWeight:style}} {...{key}}>{part}</span>
})
}
</div>
)
}
//autocomplete component
const SearchBar = () => {
const [suggestions, setSuggestions] = useState([]),
[inputValue, setInputValue] = useState(''),
onInput = input => {
setInputValue(input)
setSuggestions(vocabulary.filter(word => input.length && word.toLowerCase().includes(input.toLowerCase())))
}
return (
<div>
<input onKeyUp={e => onInput(e.target.value)} />
<div >
{
suggestions.map((suggestion,key) => <SuggestedOption {...{key,suggestion,match:inputValue}} />)
}
</div>
</div>
)
}
render(
<SearchBar />,
document.getElementById('root')
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.12.0/umd/react.production.min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.11.0/umd/react-dom.production.min.js"></script><div id="root"></div>
Here's a way to do it with string.replace:
const list = [
'end',
'England',
'engagement'
]
const boldify = (search) => {
return (string) =>
string.replace(new RegExp(search, 'gi'), '<strong>$&</strong>')
}
document.body.innerHTML = list.map(boldify('en')).join('<br>')
EDIT: After some times thinking, and seeing the accepted answer, I wanted to push the vanilla JS version to have something more complete. And moreover, the React version seemed laggy to me, this one is faster!
Faster because:
it uses vanilla JavaScript
it uses correct dom manipulation (no innerHTML)
'use strict'
const list = [
'end',
'England',
'engagement',
'Ken Bogard',
'Venom',
'Engagement'
]
const boldify = (search) => {
return (string) => {
const div = document.createElement('div')
const parts = string.split(new RegExp(`(?=${search})|(?<=${search})`, 'gi'))
div.append(
...parts.map(part => {
if (part.toUpperCase() !== search.toUpperCase()) return part
const strong = document.createElement('strong')
strong.innerText = part
return strong
})
)
return div
}
}
const contains = (search) => {
search = search.toUpperCase()
return (string) => string.toUpperCase().indexOf(search) >= 0
}
const refreshSuggestions = () => {
// Fast removal of children.
while (suggestions.firstChild) suggestions.removeChild(suggestions.firstChild)
// nothing to do
if (searchInput.value.length == 0) return
const newSuggestions =
list.filter(contains(searchInput.value))
.map(boldify(searchInput.value))
suggestions.append(...newSuggestions)
}
searchInput.addEventListener('input', refreshSuggestions)
<input id="searchInput" list="suggestions">
<div id="suggestions">
</div>
EDIT - Yevgen's answer is much nicer than this one.
A simple loop can achieve this if i understand correctly what you want:
var array = ["end","engagement","Engagement","england","enough","not this","or this"];
function filterArray(array, id) {
var returnArray = [];
for (var i = 0; i < array.length; i++) {
value = array[i];
if (value.includes(id)) {
returnArray.push(value);
}
}
return returnArray;
}
var filteredArray = filterArray(array,"en");
console.log(filteredArray);
If you wanted to ensure duplicates (where case on Engagement causes a duplicate) you could set the string to lowercase before pushing to the array, and check the array for the existence of the string before pushing again.

Categories

Resources