how to render component from .map() in react - javascript

So im trying to not duplicate code and probably over complicating this but im very curious if there is a way for a system like this to work
<Drawer anchor="right" open={sideBarOpen} onClose={toggleSideBar}>
<List className={classes.sideBar}>
{[
["test1", <LockOpenIcon />],
["test2", <LockOpenIcon />],
["test2", <LockOpenIcon />],
].map(({ text, icon }, index) => (
<Fragment key={index}>
<ListItem button>
<ListItemIcon>{icon}</ListItemIcon>
<ListItemText primary={text} />
</ListItem>
<Divider />
</Fragment>
))}
</List>
</Drawer
where I map over an array of pairs of [text, iconComponent] and then render the iconComponent in the following element. this is producing no errors (but also not rendering anything in the ) and if theres not a way then thats cool but any help would be appreciated.

Yes, it's possible, and you've mostly done it right. You just used object destructuring ({text, icon}) where you should have used iterable destructuring ([text, icon]):
<Drawer anchor="right" open={sideBarOpen} onClose={toggleSideBar}>
<List className={classes.sideBar}>
{[
["test1", <LockOpenIcon />],
["test2", <LockOpenIcon />],
["test2", <LockOpenIcon />],
].map(([ text, icon ], index) => (
<Fragment key={index}>
<ListItem button>
<ListItemIcon>{icon}</ListItemIcon>
<ListItemText primary={text} />
</ListItem>
<Divider />
</Fragment>
))}
</List>
</Drawer>
However, if the values are hardcoded like that, you might consider abstracting the repeated part of that into its own component:
const MyListItem = React.memo(({text, icon}) => (
<ListItem button>
<ListItemIcon>{icon}</ListItemIcon>
<ListItemText primary={text} />
</ListItem>
));
(The React.memo is just because I figure this doesn't change unless text or icon changes.)
Then it's:
<Drawer anchor="right" open={sideBarOpen} onClose={toggleSideBar}>
<List className={classes.sideBar}>
<MyListItem text="test1" icon={<LockopenIcon/>} />
<Divider />
<MyListItem text="test2" icon={<LockopenIcon/>} />
<Divider />
<MyListItem text="test3" icon={<LockopenIcon/>} />
</List>
</Drawer>

Related

unable to set state in react for provided situation

i am building a ecommerce web app with react and i am unable to set state profileOptionsLayer when i click on the highlighted listitem both the logs are displayed on the console but component dosent rerender and state "profileOptionsLayer" is not updated either ,i am unable to locate the reason need help!
ALL imports .....
const TemporaryDrawer = ({
profileDrawerlayer,
setprofileDrawerlayer,
setuserDetails,
userDetails,
}) => {
const [profileOptionsLayer, setprofileOptionsLayer] = useState();
console.log(profileOptionsLayer);
return (
<>
<Drawer
anchor={"right"}
open={profileDrawerlayer}
onClose={() => {
setprofileDrawerlayer(false);
}}
>
<Box
sx={{ width: 250, background: "lightblue", height: "100%" }}
role="presentation"
onClick={() => {
setprofileDrawerlayer(false);
}}
onKeyDown={() => {
setprofileDrawerlayer(false);
}}
>
<List>
////////////////////////////////////// Below ///////////////////////////////////////////////
<ListItem disablePadding>
<ListItemButton
onClick={() => {
console.log("dsadsa");
setprofileOptionsLayer("View"); <= unable to set this state
console.log(profileOptionsLayer);
console.log("dsadsa");
}}
>
<ListItemIcon>
<VisibilityIcon />
</ListItemIcon>
<ListItemText primary={"View Profile"} />
</ListItemButton>
</ListItem>
/////////////////////////////////////// UP /////////////////////////////////////////
<ListItem
disablePadding
onClick={() => {
localStorage.removeItem("userId");
setuserDetails("");
}}
>
<ListItemButton>
<ListItemIcon>
<LogoutIcon />
</ListItemIcon>
<ListItemText primary={"Logout"} />
</ListItemButton>
</ListItem>
</List>
<Divider />
</Box>
</Drawer>
{profileOptionsLayer &&<ProfileOptions {...{ userDetails }} />}
</>
);
};
export default TemporaryDrawer;

How to show array elements in List Item Text

I am new to react. I am using ListIemText to show values in the screen. My question is how do I use List Item text to show all the elements of an array in a list.
Here is the code. Kpi_Before is the array element.
<MuiThemeProvider theme={theme}>
<React.Fragment>
<AppBar size>Confirm Information</AppBar>
<br />
<br />
<List>
<ListItemText primary="Title" secondary={Title} />
<ListItemText
primary="Details of Best Practice"
secondary={Details}
/>
<ListItemText
primary="What is the Best Practice"
secondary={What}
/>
<ListItemText
primary="Why was the Best Practice Implemented"
secondary={Why}
/>
<ListItemText
primary="How was the Best Practice Implemented"
secondary={How}
/>
<ListItemText primary="Implementation Status" secondary={Status} />
<ListItemText primary="Cost of Implementation" secondary={Cost} />
<ListItemText
primary="Benefits of the Best Practice"
secondary={Benefits}
/>
<ListItemText
primary="Time taken for Implementation"
secondary={Time}
/>
<ListItemText
key={idx}
primary="KPI Before Implementation"
secondary={Kpi_Before}
/>
<ListItemText primary="UOM_Before" secondary={UOM_Before} />
<ListItemText primary="Base_Before" secondary={Base_Before} />
<ListItemText primary="Base_Before" secondary={Target_Before} />
<ListItemText primary="Target Date" secondary={dateTime} />
</List>
</React.Fragment>
</MuiThemeProvider>
If I'm understanding what you're wanting. You could do something like:
<div>
<ul>{Kpi_Before.map(item => <li key={item}> {item} </li>)}</ul>
</div>
This will iterate through the Kpi_Before array. When using .map in React you should have a key for every item so that you need to reference it to remove it using .filter or some other method, React can find it. If for some reason you have equal values in the array you could do something like this instead:
<div>
<ul>{Kpi_Before.map(item => <li key=Date.now()> {item} </li>)}</ul>
</div>

Returning nightmare with .map() inside of a .map()

I've been at this for a while. So I have some nested navigation json that I am using. The top level navigation is loading fine (the nav.map) once I move further down the rabbit hole I find myself not returning the top level or the sub level navigation. Everything compiles successfully. Am I just missing it?
return(
<List component="nav" className={classes.root}>
{nav.map(function(element) {
<ListItem
button
onClick={handleClick}
id={element.toplevel}
key={element.toplevel}
>
<ListItemText primary={element.toplevel} />
{open ? <ExpandLess /> : <ExpandMore />}
</ListItem>;
return element.children.map(function(child) {
return (
<Collapse timeout="auto" unmountOnExit>
<List component="div" disablePadding>
<ListItem button className={classes.nested}>
<ListItemIcon>
<StarBorder />
</ListItemIcon>
<ListItemText primary={child.name} />
</ListItem>
</List>
</Collapse>
);
});
})}
</List>
);
Make sure that your map callback functions actually return the jsx code. Your return statements are not set right.
One way that is commonly used in react-land to make the jsx code more readable is the arrow function syntax. This way you get rid of the return statements and return the whole function body (it's just syntactic sugar).
Next thing: be aware of your closing tags. I just assumed that your list item of the element object closes after the ListItemText tag and that your second map function opens a new list item after your element ListItem. jsx only lets you return one root tag at a time. This is why (as the comment below has suggested) using an empty <> ... </> tag pair as a root element will solve this issue.
return (
<List component="nav" className={classes.root}>
{data.nav.map((element) => (
<>
<ListItem
button
onClick={handleClick}
id={element.toplevel}
key={element.toplevel}
>
<ListItemText primary={element.toplevel} />
{open ? <ExpandLess /> : <ExpandMore />}
</ListItem>
{
element.children.map((child) => (
<Collapse timeout="auto" unmountOnExit>
<List component="div" disablePadding>
<ListItem button className={classes.nested}>
<ListItemIcon>
<StarBorder />
</ListItemIcon>
<ListItemText primary={child.name} />
</ListItem>
</List>
</Collapse>
))
}
</>
))}
</List>
);
Sandbox link: https://codesandbox.io/s/material-demo-uv7c7?fontsize=14&hidenavigation=1&theme=dark
Try this, i have restructured it a bit.
<List component="nav" className={classes.root}>
{nav.map(function(element) {
return(
<ListItem
button
onClick={handleClick}
id={element.toplevel}
key={element.toplevel}
>
<ListItemText primary={element.toplevel} />
{open ? <ExpandLess /> : <ExpandMore />}
</ListItem>
{element.children.map(function(child) {
return (
<Collapse timeout="auto" unmountOnExit>
<List component="div" disablePadding>
<ListItem button className={classes.nested}>
<ListItemIcon>
<StarBorder />
</ListItemIcon>
<ListItemText primary={child.name} />
</ListItem>
</List>
</Collapse>
)
})
}
}))}
</List>
);

React: If statements inside a map function

I'm struggling with this for a while now, I'm building a page so the user can create forms dynamically I have one react component that creates the forms and one that renders it, I'm currently facing problems with editing the forms already created.
const GigApplicationRenderer = (props) => {
const {
questions,
onSelectDelete,
onSelectEdit,
classes,
theme,
handleClickOpen,
handleClose,
open
} = props;
return (
<Fragment>
{questions.map((element, index) => (
<div className={classes.li} key={element.title}>
<Grid container spacing={24}>
<Grid item xs={12}>
{element.type === 'input' ? (
<Paper index={index} className={classes.paper}>
<Title
onSelectEdit={onSelectEdit}
onSelectDelete={onSelectDelete}
element={element}
handleClose={handleClose}
open={open}
/>
<TextField
className={classes.textField}
value=""
label="answer text"
margin="normal"
/>
</Paper>
) : element.type === 'radiobox' ? (
<Paper className={classes.paper}>
<Title
onSelectEdit={onSelectEdit}
onSelectDelete={onSelectDelete}
element={element}
handleClose={handleClose}
open={open}
/>
{element.options.map(option => (
<ListItem key={option} className={classes.itemsList}>
<RadioGroup>
<FormControlLabel
label={option}
value={option}
control={<Radio color="primary" />}
/>
</RadioGroup>
</ListItem>
))}
</Paper>
) : element.type === 'checkbox' ? (
<Paper className={classes.paper}>
<Title
onSelectEdit={onSelectEdit}
onSelectDelete={onSelectDelete}
element={element}
handleClose={handleClose}
open={open}
/>
{element.options.map(option => (
<ListItem key={option} className={classes.itemsList}>
<FormControlLabel
label={option}
value={option}
control={<Checkbox checked={false} color="primary" />}
/>
</ListItem>
))}
<Divider light />
</Paper>
) : null}
</Grid>
</Grid>
</div>
))}
</Fragment>
);
};
this is the component that renders the code, it has some if statements that check the properties of the forms created and renders it accordingly.
this is the title component:
const Title = (props) => {
const {
element, onSelectDelete, onSelectEdit, index, handleClose, open
} = props;
return (
<Grid container spacing={24}>
{console.log(element.title)}
<Grid item xs={12} sm={9}>
<Typography style={{ textAlign: 'left' }} variant="subheading" gutterBottom>
{element.title}
</Typography>
</Grid>
<Grid item xs={12} sm={3}>
<Tooltip enterDelay={100} leaveDelay={100} placement="bottom" title="Edit">
<IconButton onClick={onSelectEdit(element, index)} aria-label="Edit">
<EditIcon />
</IconButton>
</Tooltip>
<Tooltip enterDelay={100} leaveDelay={100} placement="bottom" title="Delete">
<IconButton aria-label="Delete">
<DeleteIcon onClick={onSelectDelete(element)} />
</IconButton>
</Tooltip>
</Grid>
<Dialog open={open} onClose={handleClose} aria-labelledby="form-dialog-title">
<DialogTitle id="form-dialog-title">
blabla
</DialogTitle>
<DialogContent>
<DialogContentText>
To subscribe to this website, please enter your email address here. We will send updates
occasionally.
</DialogContentText>
<TextField
autoFocus
// value={element.title}
margin="dense"
label="question title"
fullWidth
/>
</DialogContent>
<DialogActions>
<Button onClick={handleClose} color="primary">
Cancel
</Button>
<Button onClick={handleClose} color="primary">
Save
</Button>
</DialogActions>
</Dialog>
</Grid>
);
};
I want it to open a dialog with the values of the element clicked so the user could edit it, the problem is that no matter which button was clicked, on the dialog, it always renders the last element of the form.
On the function onSelectEdit though, I get the correct element, I think it is happening because of the if statements of the first component.
the questions array look like this:
questions: [
{
title: 'Why do you want this job?',
type: 'input'
},
{
title: 'Do you know Javascript?',
type: 'radiobox',
options: ['yes', 'no']
},
{
title: 'What techs your know?',
type: 'checkbox',
options: ['html', 'css']
}
],
is there a way to render the correct element when the dialog opens?
I'm also open to any suggestion on how to accomplish this.

How to avoid DRY code with React Components

Currently Using React with Material UI v1.0 in implementing a list but I don't want to repeat my code.
The existing Code looks like this.
import List from 'material-ui/List';
import DashboardIcon from 'material-ui-icons/Dashboard';
import BuildIcon from 'material-ui-icons/Build';
import Listings from './BarComponents';
function SideBar() {
return (
<div>
<List>
<ListItem button>
<ListItemIcon>
<DashboardIcon />
</ListItemIcon>
<ListItemText primary="Dashboard" />
</ListItem>
<ListItem button>
<ListItemIcon>
<BuildIcon />
</ListItemIcon>
<ListItemText primary="Control Panel" />
</ListItem>
</List>
</div>
);
}
export default SideBar;
I want to get avoid repeating creating the list items so i've created a new file and passed the props into, code is below.
import React from 'react'
import { ListItem, ListItemIcon, ListItemText } from 'material-ui/List';
export default function Listings(props) {
return(
<div>
<ListItem button>
<ListItemIcon>
<props.icon />
</ListItemIcon>
<ListItemText primary={props.prim} />
</ListItem>
</div>
);
}
And also this
<Listings icon={DashboardIcon} prim="Dashboard" />
<Listings icon={BuildIcon} prim="Build" />
Into the original file for a replacement of
<ListItem button>
<ListItemIcon>
<DashboardIcon />
</ListItemIcon>
<ListItemText primary="Dashboard" />
</ListItem>
<ListItem button>
<ListItemIcon>
<BuildIcon />
</ListItemIcon>
<ListItemText primary="Control Panel" />
</ListItem>
Is the best way to pass a component e.g though
and call it via Thanks in advance.
You can use a dynamic component.
renderElement(name, props = {}) {
var MyComponent = name
return <MyComponent {...props} />;
}
render() {
return(
<div>
<ListItem v-for="item in list" key={item.id} button={item.button}>
<ListItemIcon>
{renderElement(props.icon)}
</ListItemIcon>
<ListItemText primary={props.prim} />
</ListItem>
</div>
);
}

Categories

Resources