Looking for a better algorithm (conditional rendering) - javascript

On my webapplication I use a json file to store my different routes, and I loop over an array inside this json file to create content, such navbar buttons.
The problem I face is that I would like to use a different icon for each value displayed from the json file, and I cannot store the icon component into the json file.
Here is my sample of code with the loop on the array contained in the json file
{props.buttons.button.map((button, index) => (
<div key={index}>
{(button.isAuth === props.isAuth || button.isAuth === 3) && (
<>
<Link to={button.to} className="link">
<ListItem button>
<ListItemIcon>
<InputIcon />
</ListItemIcon>
<ListItemText primary={button.label} />
</ListItem>
</Link>
</>
)}
</div>
))}
Here is a sample of the json file
{
"button": [
{
"label": "Home",
"isAuth": 3,
"to": "/"
},
{
"label": "About",
"isAuth": 3,
"to": "/about"
}
]
}
Currently, for each data on my json file, always the same icon component is displayed : <InputIcon />.
How can I display a different icon component for each data from the json file (such as <HomeIcon /> for the home button, <LogInIcon /> for the login one, ...) without using and if/else
Thanks

If possible you can structure the <InputIcon /> component to accept a prop and pass the label to this component.

Related

How can I access the values of the props in my component?

I have this jsx component ArticleListItem which receives data as props and displays it. When I run my web app, I get this error: Objects are not valid as a React child. How should I handle the props to access them the way I'm trying.
Here's the ArticleListItem component which throws the error:
import { Link } from 'react-router-dom';
import { Button, Icon, Item, Segment, SegmentGroup } from 'semantic-ui-react';
export default function ArticleListItem(props) {
return(
<SegmentGroup>
<Segment>
<Item>
<Item.Header>
{props.article.title}
</Item.Header>
<Item.Content>
{props.article.description}
</Item.Content>
</Item>
</Segment>
<Segment>
<span>
<Icon name='clock' /> {props.article.publishedAt}
<Icon name='newspaper' /> {props.article.author}
</span>
</Segment>
<Segment>
<span>
<Icon name='globe' /> <Button>
as={Link}
to={props.article.url}
color='teal'
floated='right'
content='View' </Button>
</span>
</Segment>
</SegmentGroup>
)
}
Here's an example of the props
{
"source": {
"id": "business-insider",
"name": "Business Insider"
},
"author": "Diamond Naga Siu",
"title": "See how much Apple pays engineers, analysts and thousands of others",
"description": "Insider analyzed thousands of Apple's H-1B visa applications to get a sense of how much it pays employees.",
"url": "http://www.businessinsider.com/see-how-much-apple-pays-engineers-analysts-and-thousands-others-2022-9",
"urlToImage": "https://i.insider.com/633222528345c90018de0060?width=1200&format=jpeg",
"publishedAt": "2022-09-28T12:00:00Z",
"content": null
}
And here's the code block where I'm passing the props to this component:
return (
<>
{props.articles && props.articles.map(article => (<ArticleListItem key={article.title} article={article} />
))}
</>
)
The idea of my web app is to fetch news from my api and to show them. I get an array of news, map over them and create an ArticleListItem for each of them.
Any help appreciated!
Your props are not inside the button tag. The ">" tag was misplaced
<Button
as={Link}
to={props.article.url}
color='teal'
floated='right'
content='View'>
Test
</Button>

Reduce code redundancy with multiple buttons using React

I have a series of icon buttons in which most properties are common. The only thing that changes is the icon type and onClick functions. Currently, I am implementing it in the following manner:
const listOfButtons = <div>
<IconButton
flat
icon={<AddIcon />}
onClick={doSomething}
primary
/>
<IconButton
flat
icon={<EditIcon />}
isDisabled
primary
/>
<IconButton
flat
icon={<TrashIcon />}
isDisabled
primary
/>
<IconButton
flat
icon={<RefreshIcon />}
primary
/>
</div>;
I want to try to reduce the redundancy of the code and try to define only one IconButton object and pass different icons and different onClicks to it as in future there might be more icon buttons. I am stuck as to how to assign onclick to a particular icon button. Is there an efficient way of defining multiple icon buttons and assigning icons and onclicks than what I have done above? Please help!
You could create an array of the changing attributes and then use Array.map.
const buttonData = [
{
icon: <AddIcon />,
onClick: doSomething
},
{
icon: <EditIcon />,
isDisabled: true
},
{
icon: <TrashIcon />,
isDisabled: true
},
{
icon: <RefreshIcon />
}
];
const listOfButtons = <div>
{
buttonData.map((data, index) => (
<IconButton
key={index}
flat
icon={data.icon}
onClick={data.onClick}
isDisabled={data.isDisabled}
primary
/>
))
}
</div>;
NOTE: When mapping React components React requires a key attribute on the root component returned from the map. This is so that React can keep track of which component is which (in the case of reordering or removing an item from the array). It is preferred to use a unique identifier from the item being mapped (as this will remain the same if the item changes position), but if you are in full control of the array in use and can ensure that no item ever changes index then using index is fine. If you use index and the items do change position it will result in more DOM changes than necessary.
Step 1: Create a separate component for button :
class IconButtons extends React.Component{
render(){
return(
<IconButton
flat
icon={this.props.icon}
primary
onClick = {this.props.onClick}
/>
)
}
Step 2 :
const buttonList = [
{
icon: <AddIcon />,
onClick: doSomething
},
{
icon: <EditIcon />,
isDisabled: true
},
{
icon: <TrashIcon />,
isDisabled: true
},
{
icon: <RefreshIcon />
}
];
const listOfButtons =
<div>
{
buttonList .map((ele,index)=>(<IconButtons
key=index
icon={ele.icon}
onClick={ele.onClick}
/>))
}
</div>;
Use Array.map to make it better
const iconsList = [<AddIcon />,<EditIcon />,<TrashIcon />,<RefreshIcon />]
const listOfButtons =
<div>
{
iconsList.map((icon,index)=>(<IconButton
key=index
flat
icon={icon}
onClick={doSomething}
primary
/>))
}
</div>;
If I understand your question, you are looking for a DRYer solution. The solutions so far offer mapping from a config, which is certainly a viable option. One alternative option would be to use composition. A quick analysis of your list of buttons show the common props between them all are flat and primary; the remaining props can change with some variability. So you could define a <FlatPrimaryButton /> like so:
const FlatPrimaryButton = ({props}) => (
<IconButton
flat
primary
{...props}
/>
);
This would allow you to write something more like this for your list:
const listOfButtons = <div>
<FlatPrimaryButton
icon={<AddIcon />}
onClick={doSomething}
/>
<FlatPrimaryButton
icon={<EditIcon />}
isDisabled
/>
<FlatPrimaryButton
icon={<TrashIcon />}
isDisabled
/>
<FlatPrimaryButton
icon={<RefreshIcon />}
/>
</div>;
This solution could then be combined with the other .map based solutions.

Routing for persistant drawer in material UI

I want to use this as a navigation for my page ,
but i cant find out how to Link in the index properly,
I already tried to put l the items apart but that didnt work
whit ,,
and
<List>
{['Home' , 'About', 'Services', 'Contact'].map((text, index) => (
<ListItem button key={text} >
<ListItemIcon>
{index === 0 && <HomeOutlinedIcon /> }
{index === 1 && <InfoOutlined/>}
{index === 2 && <SettingsOutlinedIcon />}
{index === 3 && <MailIcon/>}
</ListItemIcon>
<ListItemText primary={text} />
</ListItem>
))}
</List>
Can someone, Show me how to correctly route my navigation in this list.
Just create pages for 'Home' , 'About', 'Services', 'Contact' and use the navigate function from reach-router.
You just have to declare your routes in the app.js with your path="/app/home" etc and the component you want to render to the DOM, then use navigate("/app/home") so you should change your array to an object with the good route on each item like this
const fieldsNames = [
{ value: "Home", icon: <SvgIcon component={SearchIcon} />, path: "/app/home" },
]
and map on those item with an onClick.

Each child in a list should have a unique key prop

Here is a link to a working example of the site: https://codesandbox.io/s/eloquent-kapitsa-kk1ls
The issue I am having is with Buttons.js:
import React, { Component } from "react";
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
import registerIcons from "./FontAwesome";
registerIcons();
const DATA = [
{
href: "https://github.com/",
icon: ["fab", "github"],
label: "Github"
},
{
href: "https://www.linkedin.com/in//",
icon: ["fab", "linkedin"],
label: "LinkedIn"
},
{
href: "...",
icon: ["fas", "file-alt"],
label: "Resume"
},
{
href: "mailto:",
icon: ["fas", "paper-plane"],
label: "Email me"
}
];
const Icon = ({ href, icon, label }) => {
return (
<span className="button">
<a href={href} target="_self" rel="noopener noreferrer">
<FontAwesomeIcon className="icon" icon={icon} size="3x" />
<span className="icon_title">{label}</span>
</a>
</span>
);
};
class Buttons extends Component {
render() {
return (
<div>
{DATA.map(props => (
<Icon {...props} />
))}
</div>
);
}
}
export default Buttons;
I have looked through the other topics related to this issue and none are analogous to my case. I am not passing parameters to the render method - just taking an existing variable and mapping it to produce my output.
I have tried changing the Buttons render method to the code below in addition to a few other permutations of the DATA array without much progress.
<div key={DATA.label}>
{DATA.map(props => (
<Icon {...props} />
))}
</div>
I have also read through the React documentation on keys with no success.
you should change the code with this:
<div>
{DATA.map((props,i) => (
<Icon key={i} {...props} />
))}
</div>
As it is advised not to use index as a key for component. I would say add a random number and do something like below
<div>
{DATA.map((props,i) => (
<Icon key={Math.random()*1000*i} {...props} />
))}
</div>
This is rather a generic approach if you don't have any unique key in your object.
Try
<Icon {...props} key={props.href} />
From react documentation
A good rule of thumb is that elements inside the map() call need keys.
Please take a look to this documentation
https://reactjs.org/docs/lists-and-keys.html#extracting-components-with-keys
The key needs to go on like this:
<div key={DATA.label}>
{DATA.map(props => (
<Icon key={props.label} {...props} />
))}
</div>
As you probably saw in the React documentation on keys you reference, they say
The best way to pick a key is to use a string that uniquely identifies a list item among its siblings.
Each of the objects in your DATA array have unique strings and you can use any of them key={props.href} or key={props.icon[1]}. I used props.label in the answer above but the point is it doesn't have to be totally unique in all the world, it just needs to be unique compared to the other items in the list.

Decoupling delete modal from button?

I've put together a sample I am working on where I want to decouple my dialogue Modal from the delete button. I'm running into trouble trying to think of how I can restructure the components while still keeping functionality. Ideally the modal should be rendered once and be modular. Data passed into it should change during the request to show the modal.
https://stackblitz.com/edit/react-l8hbdr
Right now I believe a delete modal is created for every single button which is horribly inefficient. I've heard using references are bad and using Redux for this seems overkill. Maybe I should use a React portal?
Where should the modal be rendered from? I'd like a reusable modal and I found this guide, however I don't know where I should be storing the Modal component and how data should pass into it.
There is a problem with props drilling in your code
For example, expenses and updateExpenses are passed from <Main /> to DisplayExpenses and then passed to <DeleteExpense />, if the app is getting bigger, this will make the code hard to maintain. I think you can put const [expenses, updateExpenses] = React.useState(...) from <Main /> to <DisplayExpenses />.
import React from 'react'
import { Col, Card, Row, Button } from "antd";
import uuid from 'react-uuid'
import 'antd/dist/antd.css';
import addExpense from '../utilities/AddExpense';
import DeleteExpense from '../utilities/DeleteExpense';
const Sentencer = require('sentencer');
export default function DisplayExpenses(props) {
// move expenses from <Main /> to <DisplayExpenses />
const [expenses, updateExpenses] = React.useState([
{
"id": uuid(),
"name": Sentencer.make("{{ noun }}"),
"amount": 53.22,
},
{
"id": uuid(),
"name": Sentencer.make("{{ noun }}"),
"amount": 76.16,
},
{
"id": uuid(),
"name": Sentencer.make("{{ noun }}"),
"amount": 716.16,
},
]);
const ListOfExpenses = () => {
return expenses.map((index) => {
return (
<Col key={index.id}>
<Card
key={index.id}
title={index.name}
bordered={true}
style={{ width: "100%", border: "1px solid black" }}
actions={[
<DeleteExpense
key={index.id}
id={index.id}
name={index.name}
amount={index.amount}
updateExpensesFn={updateExpenses}
expenses={expenses} />
]}
>
<p key={index.id}>Amount: {index.amount}</p>
</Card>
</Col>
)
})
}
return (
<>
<Row gutter={[16, 8]}>
<ListOfExpenses />
<Button
type="primary"
style={{ margin: "5% 5%" }}
onClick={() => addExpense({ expenses, updateExpensesFn: updateExpenses})}
>
Add Expense
</Button>
</Row>
</>
);
}
if you want to render <DeleteModal /> only once instead of every single button, you can adjust <DeleteExpense /> slightly, make <DeleteModal /> rendered only when modalVisibleBool is true
{modalVisibleBool && (
<DeleteModal
showModal={modalVisibleBool}
onOkay={() => onConfirmDelete({
updateExpensesFn: props.updateExpensesFn,
expenses: props.expenses,
expenseToDelete
})}
cancelFn={() => onCancelDeleteModal()}
expenseToDelete={expenseToDelete}
/>
)}

Categories