Double conditional rendering in react? - javascript

I'm building a small clothing store app in React, just to learn things. I implemented a filter by price section but I would want to be able to write a condition for the case in which there's no item in the selected price range, instead of the page being blank.
render() {
const filteredOpt2 = this.state.items.filter(item => item.price <= 60);
const filteredOpt3 = this.state.items.filter(item => item.price >= 60 && item.price <= 100);
return (
<div>
{
this.state.selectedOption === "option1"
? <ProductList products={this.state.items} style={{marginLeft: 0}}/>
: this.state.selectedOption === "option2"
? <ProductList products={filteredOpt2} style={{marginLeft: 0}}/>
: this.state.selectedOption === "option3"
? <ProductList products={filteredOpt3} style={{marginLeft: 0}}/>
: null
}
</div>
)
}
I know the code is very repetitive and really not ideal, but I couldn't come up with a better approach for now.
So what I wish to do, is, let's say the filteredOpt2 results in an empty array, how and where could I implement a condition that says if this happens, display a p tag showing a text?

If not diving deep in the idea of component split you can just add condition in the template like this
const filteredOpt2 = this.state.items.filter((item) => item.price <= 60);
const filteredOpt3 = this.state.items.filter(
(item) => item.price >= 60 && item.price <= 100
);
return (
<div>
{this.state.selectedOption === "option1" ? (
<ProductList products={this.state.items} style={{ marginLeft: 0 }} />
) : this.state.selectedOption === "option2" ? (
<>
{filteredOpt2.length > 0 ? (
<ProductList products={filteredOpt2} style={{ marginLeft: 0 }} />
) : (
<p>No items lower than 60</p>
)}
</>
) : this.state.selectedOption === "option3" ? (
<>
{filteredOpt3.length > 0 ? (
<ProductList products={filteredOpt3} style={{ marginLeft: 0 }} />
) : (
<p>No items between 60 and 100</p>
)}
</>
) : null}
</div>

You are sending the product list down to ProductList component via props. In that component, where you use your props.products, you can add something like this:
{!props.products.length
? <p>No product matched the criteria</p>
: props.products.map(product => {... whatever your code is })
}
To elaborate, if the products.length is zero, you show your 'no product' text, otherwise, show the products.

You can do the filtering in advance, e.g. in a function and then render the list accordingly:
const filterItems = (items, filter) => {
if (filter === "option1") return items;
if (filter === "option2") return items.filter(item => item.price <= 60);
if (filter === "option3") return items.filter(item => item.price >= 60 && item.price <= 100);
};
render() {
const filtered = filterItems(this.state.items, this.state.selectedOption);
return (
<div>
{filtered.length === 0 ? (
<p>No products</p>
) : (
<ProductList products={filtered} style={{marginLeft: 0}}/>
)}
</div>
);
}
or even better let the ProductList component handle that:
render() {
return (
<div>
<ProductList
products={filterItems(this.state.items, this.state.selectedOption)}
style={{marginLeft: 0}}
/>
</div>
);
}
const ProductList = ({products}) => {
if (products.length === 0) return <p>No products</p>;
return ...
};

Related

How to handle arrays in a Grommet DataTable?

I'm trying to use arrays in Grommet DataTable. My data looks like this :
{
customer: [
'BANANA',
'Banana',
'banana',
'republic of banana'
],
somethingelse: ['ABC','123','DEF']
}
In a regular Grommet Table , I'm able to use every cell by defining the first value from the array as title - for example customer[0] - and create an expandable arrow to show the rest of the data in 'customer' :
But I don't get how to do this on a cell basis for a Grommet DataTable ?
Here is the way I'm using it in the regular Grommet Table :
<TableCell scope="row" pad={{ left: '2px', righ: '3px' }}>
<TextInput name="tags" size="xsmall" />
</TableCell>
</TableRow>
{searchResults.length > 0 &&
searchResults.map((searchResult, index) => (
<TableRow key={index}>
<TableCell>
<Box direction="row">
<Text size="xsmall">{searchResult.customer[0]}</Text>
{searchResult.customer.length > 1 && (
<Button
plain
hoverIndicator={false}
icon={
isExpanded[index] ? (
<FormDown size="18px" />
) : (
<FormNext size="18px" />
)
}
onClick={() => toggleOpen(index)}
/>
)}
</Box>
<Box>
{isExpanded[index] && listElements(searchResult.customer)}
</Box>
</TableCell>
Here is my Form , using DataTable :
return (
<Form value={formData} onSubmit={onSubmit} onChange={onChange}>
...
<DataTable
fill
border={{ body: 'bottom' }}
paginate
columns={columns}
data={searchResults}
select={select}
onClickRow={(e) => console.log(e.datum)}
onSelect={() => {}}
step={8}
rowDetails={(row) => { // I'm able to use rowDetails to expand and display some data , but how can I use this to 1. Use the [0] element of the array as title and 2. apply to all cells in the row/table.
for (const cell in row) {
// if (cell.length > 1) {
// return listElements(cell);
// }
console.log(cell);
}
}}
...
/>
...
</Form>
);
I was able to achieve that by using the render function and passing a CellElement to it, in which I have created my rules :
const columns = [
{
property: 'customer',
header: <FormField label="Customer" name="customer" size="xsmall" />,
render: (datum) => <CellElement val={datum.customer} />,
},
CellElement.js
import { Box, Text, Button } from 'grommet';
import { FormNext, FormDown } from 'grommet-icons';
import React, { useState } from 'react';
const CellElement = ({ val }) => {
const title = Array.isArray(val) ? val[0] : val;
const [isExpanded, setIsExpanded] = useState({});
const toggleOpen = (category) => {
setIsExpanded({
...isExpanded,
[category]: !isExpanded[category],
});
};
const listElements = (arr) => {
return arr.slice(1).map((el, index) => (
<Text key={index} size="xsmall">
{el}
</Text>
));
};
return (
<Box>
<Box direction="row">
<Text size="xsmall">{title}</Text>
{Array.isArray(val) && val.length > 1 && (
<Button
plain
hoverIndicator={false}
icon={
isExpanded[title] ? (
<FormDown size="18px" />
) : (
<FormNext size="18px" />
)
}
onClick={() => toggleOpen(title)}
/>
)}
</Box>
<Box>{isExpanded[title] && listElements(val)}</Box>
</Box>
);
};
export default CellElement;

hiding only the selected component

hides only the selected component. I am unable to hide only a specific component
useEffect(() => { result() }, []);
const dataComponent = state.data.map((video, index) => {
return <>
<p onClick={() => showMoreInfo(!showing)}>show</p>
{showing
? <ContainerFilmHome key={index} name={video.show_name} pictures={video.pictures}/>
: null}
</>
})
You need update showing to store index of item selected:
onClick={() => showMoreInfo(index === showing ? null : index)}
{showing === index
? <ContainerFilmHome key={index} name={video.show_name} pictures={video.pictures}/>
: null}

React spring auto height not working properly

React spring auto height not working properly.
I am trying to create animation for auto height with react spring, I already achieved the animation but if I go to another page and go back to the page that I have auto height animation it's not working until I refresh the page. How can I fix this problem?
Here is my code:
const defaultHeight = "100px";
const [contentHeight, setContentHeight] = useState(defaultHeight);
const [ref, { height }] = useMeasure();
const expand = useSpring({
config: { ...config.default, duration: 250 },
height: `${contentHeight}px` || defaultHeight,
});
useEffect(() => {
setContentHeight(height);
window.addEventListener("resize", setContentHeight(height));
return window.removeEventListener("resize", setContentHeight(height));
}, [height]);
return (
<div className="team-management">
<div className="team-management-tabs-header">
<div className="team-management-tab-items">
{tabs.map((tab, index) => (
<div
id={editable === true ? "" : `${tab}`}
className={
activeTab === index
? "team-management-tab-item selected"
: "team-management-tab-item"
}
key={tab}
role="button"
tabIndex={tab}
onKeyPress={() => {
return;
}}
onClick={() => {
if (editable === true) {
setActiveTab(index);
} else if (index !== 5 && index !== 6) {
setActiveTab(index);
}
}}
>
{teamData.type === "organization" ? (
<span className="tab-item-text">{tab}</span>
) : (
<>
{tab !== "Sub-Team" && (
<span className="tab-item-text">{tab}</span>
)}
</>
)}
<span className="tab-item-indicator" />
</div>
))}
</div>
</div>
<animated.div style={expand}>
<div className="team-management-tab-panes" ref={ref}>
{tabs[activeTab] === "Mission" && (
<Mission
editable={editable}
teamId={teamId}
teamData={teamData}
fetchTeamData={fetchTeamData}
notify={notify}
/>
)}
{tabs[activeTab] === "Managers" && <Managers members={members} />}
{tabs[activeTab] === "Members" && (
<Members
members={members}
teamId={teamId}
tab={tabs[activeTab]}
userPublicId={userPublicId}
fetchTeamData={fetchTeamData}
notify={notify}
/>
)}
{tabs[activeTab] === "Invitees" && (
<Invitees
teamData={teamData}
teamId={teamId}
privateTeamId={teamData.id}
fetchTeamData={fetchTeamData}
tab={tabs[activeTab]}
/>
)}
{tabs[activeTab] === "Sub-Team" && (
<SubTeam
teamId={teamId}
teamData={teamData}
members={members}
fetchTeamData={fetchTeamData}
/>
)}
</div>
</animated.div>
</div>
);
Thank you for helping me out

Add condition to arrow function

I have the below code snippet,
{categoriesWithSub.length > 0 &&
categoriesWithSub.map(item => {
//Add new condition here
const pageChanges = {
catId: item.id,
catName: item.catName,
};
return (
<Tile
to="./request"
text={item.catName}
key={item.id}
onClick={() => handleChange('departments', pageChanges)}
/>
);
})}
how can I add to the below a sub condition on item, something like that:
item.subCategories.length > 0 ?
return (...):
retrun (
<Tag
text={''}
to="../exact-request"
key={0}
onClick={() => handleChange('request', '')}
/>
I tried that but something is wrong with the syntax:
{categoriesWithSub.length > 0 &&
categoriesWithSub.map(item => {
item.subCategories.length > 0 ?(
const pageChanges = {
catId: item.id,
catName: item.catName,
};
return (
<Tile
to="./request"
text={item.catName}
key={item.id}
onClick={() => handleChange('departments', pageChanges)}
/>
);
):
retrun (
<Tag
text={''}
to="../exact-request"
key={0}
onClick={() => handleChange('request', '')}
/>
)
})}
return (item.subCategories.length > 0) ?
(...):
(
<Tag
text={''}
to="../exact-request"
key={0}
onClick={() => handleChange('request', '')}
/>
)
Iam not sure I got it completly what you wanted, but you can just add the condition there. But I would say doing this in render is bad practice, in render you should render this should be a function or component.
{
categoriesWithSub.length > 0 &&
categoriesWithSub.map(item => {
if (item.subCategories.length > 0 ) {
return something
}
return somethingElse
})
}
or if you insist on using ternars
{
categoriesWithSub.length > 0 &&
categoriesWithSub.map(item => {
return item.subCategories.length > 0 ?
something :
somethingElse
})
}
It's litle strange aproach but it's simple:
return item.subCategories.length > 0 ?
(
<Some ... />
) :
(
<Tag
text={''}
to=../exact-request
key={0}
onClick={() => handleChange('request', '')}
/>
);
But i recomend you writing code like this:
if (item.subCategories.length > 0) {
return <Some ... />
}
return (
<Tag
text={''}
to=../exact-request
key={0}
onClick={() => handleChange('request', '')}
/>)
Your code must be readable
You can return that condition in the beginning if falsey:
{categoriesWithSub.length > 0 &&
categoriesWithSub.map(item => {
const someCondition = <...>
if (!someCondition) return false;
// rest of your code

How to handle the style of one element/index in a component rendered by an array?

I need to handle the style of an element in a component based on some parameters.
I have this code:
<View>
{assignedPassengersData.map((info, index) => (
<View
key={info.id}
opacity={info.id === passengerCardId ? 1 : 0.5}
>
<PassengersInfo
id={info.id}
buttonText={
passengerCardId === info.id ? 'CONFIRM DROPOFF' : 'START NAVIGATING'
}
btnStyle={[
passengerCardId === info.id
? { backgroundColor: '#263238' }
: navigationStore.index
? { backgroundColor: Colors.pickupTabColor }
: { backgroundColor: Colors.dropOffTabColor },
]}
/>
</View>
))}
</View>
The prop I need to handle is opacity.
Here:
opacity={info.id === passengerCardId ? 1 : 0.5}
The way I have the code right now works only when that condition is met, so I can see the element I need with opacity: 1. So the rest of the elements always have opacity: 0.5. I need that when the app loads, everything should have opacity: 1. The when info.id === passengerCardId opacity should be set to 1 only in the element that mets the condition. Then the rest of the elements should have opacity set to 0.5.
So, how can I achieve what I need?
If I were you, I would create an external function.
Something like:
getOpacity = infoId => {
const {pessengerCardId} = this.props; ==> supposing your pessengerCardId comes from props;
if(passengerCardId && infoId !== pessengerCardId) {
return 0.5;
}
return 1;
}
Your code should looks like something like:
<View>
{assignedPassengersData.map((info, index) => (
<View
key={info.id}
opacity={() => this.getOpacity(info.id)}
>
<PassengersInfo
id={info.id}
buttonText={
passengerCardId === info.id ? 'CONFIRM DROPOFF' : 'START NAVIGATING'
}
btnStyle={[
passengerCardId === info.id
? { backgroundColor: '#263238' }
: navigationStore.index
? { backgroundColor: Colors.pickupTabColor }
: { backgroundColor: Colors.dropOffTabColor },
]}
/>
</View>
))}
</View>

Categories