How can I use show component inside a ListItemText? - reactjs

I used the ListItemText to display the list and my goal is to use the show component in the react-admin when each row of the list is clicked, but the function related to the display is not executed? How should it be done?
Contacts.js
/// --- List ---
export const ContactList = (props) => {
const classes = useStyles();
return (
<List className={classes.list} {...props} pagination={false} exporter={false} filters={<ContactFilter/>}>
<ContactSimpleList/>
</List>
)
};
/// --- Child list ---
const ContactSimpleList = () => {
const {ids, data} = useListContext();
const handleClick = (id) => {
ShowContact(id);
}
return (
<>
{ids.map(id => (
<ListItem key={id} button>
<ListItemAvatar>
<Avatar alt="Profile Picture" src={data[id].person}/>
</ListItemAvatar>
<ListItemText primary={data[id].name} onClick={() => handleClick(id)}/>
</ListItem>
))}
</>
);
}
/// --- Show ---
export const ShowContact = (props) => (
<Show {...props} actions={<ShowActionsOnTopToolbar/>} title={<ContactTitle/>}>
<SimpleShowLayout>
<TextField source="id"/>
<TextField source="name"/>
<TextField source="numbers.number" label="Number"/>
<TextField source="numbers.type" label="Type Call"/>
</SimpleShowLayout>
</Show>
);

This is what you need to do; replace Typography with your component
<ListItemText
disableTypography
primary={
<Typography>Pedroview</Typography>
}
/>

Related

Solve: Use a function from the child component to assign a value into the props?

In my parent component, there's a state that stores the selected task id from the child component. I used the props to pass the data from child to parent component. But my problem is I will use a function that will generate the selected task id inside the child component. The result that i want is if the user selects a button it will trigger the function that will generate the task id. Then it will store the data into props that will be pass to the parent component. The reason why the i will used a function instead of using onClick={e => e.currentTarget.getAttribute('taskkd')} I will also need to pass other props that will display the dialog box.
Parent Component
const Home = () => {
let userInfo = localStorage.getItem('userData');
const [taskDeleteId, setTaskDeleteId] = useState();
const [showPrompt, setShowPrompt] = useState(false);
useEffect(() => {
console.log(taskDeleteId);
}, [history, taskDeleteId]);
return (
<>
<Grid item xs={12} sm={8} className={baseClasses.mainTaskContainer}>
<TasksList
deleteId={(deleteId) => setTaskDeleteId(deleteId)}
showPrompt={(showPrompt) => setShowPrompt(showPrompt)}
/>
<PromptComponent showPrompt={showPrompt} shownewArr={newArr} />
</Grid>
</Grid>
</>
);
};
Child Component
const TasksList = ({ deleteId, showPrompt }) => {
const selectedTask = (e) => {
deleteId = e.currentTarget.getAttribute('taskid'); <--- the task id that i need to pass into my parent component
};
return (
<>
<Container
component='div'
maxWidth='xl'
className={classes.taskContainer}
><Container
component='div'
maxWidth='xl'
className={classes.taskContainer}
>
<Container
component='div'
maxWidth='xl'
className={classes.todoContainer}
onDragOver={(e) => onDragOverTask(e)}
onDragLeave={(e) => onDragLeaveTask(e)}
onDrop={(e) => onDropTask(e)}
>
<Typography variant='h6' className={classes.containerTitle}>
<span>To do</span>
<span>{taskTodo.length}</span>
</Typography>
{taskTodo.map((currentTask, index) => {
return (
<Card
variant='outlined'
className={classes.cardTask}
key={index}
style={{ background: '#f3f3f3', marginTop: '1rem' }}
onDragStart={(e) => onDragStartTask(e, currentTask._id)}
draggable
>
{currentTask.desc && (
<Collapse
in={expanded === `todo-panel_${index}`}
timeout='auto'
unmountOnExit
className={classes.collapsePanel}
color='primary'
>
<CardContent className={classes.descPrevContainer}>
<Typography
variant='body1'
className={classes.text}
dangerouslySetInnerHTML={createMarkup(
textTruncate(currentTask.desc, 50)
)}
></Typography>
</CardContent>
</Collapse>
)}
<CardActions
disableSpacing
className={classes.bottomActionsContainer}
>
<Tooltip title='Delete'>
<IconButton
aria-label='delete'
className={classes.BottomDelete}
taskid={currentTask._id}
onClick={(() => showPrompt(true), selectedTask)} <--- props
>
<DeleteIcon />
</IconButton>
</Tooltip>
<Tooltip title='Edit'>
<IconButton
aria-label='edit'
className={classes.BottomEdit}
taskid={currentTask._id}
>
<EditIcon />
</IconButton>
</Tooltip>
</CardActions>
</Card>
);
})}
</Container>
</>
);
};

React: How to pass the data from function located on the child component into parent component?

I have a function called handleDelete that contains the id of specific task. I want to pass the value of that function to my parent component because this will be my reference for my prompt component. The prompt component contains the title of the task that's the reason that I want to get the task id.
Parent Component
const Home = ({ history }) => {
const [taskDeleteId, setDaskDeleteId] = useState();
const [showPrompt, setShowPrompt] = useState(false);
return (
<>
<Grid>
<TasksList deleteId={(deleteId) => setDaskDeleteId(deleteId)} />
<PromptComponent showPrompt={showPrompt} taskId={taskIdref} />
</Grid>
</>
);
};
export default Home;
Child Component
const TasksList = ({ taskId, deleteId }) => {
//Function
const handleDelete = (e) => {
deleteId = e.currentTarget.getAttribute('taskId');
setAssignTask(assignTask.filter((task) => task._id !== deleteId));
};
return (
<>
<Container
component='div'
maxWidth='xl'
className={classes.taskContainer}
>
<Container
component='div'
maxWidth='xl'
className={classes.todoContainer}
onDragOver={(e) => onDragOverTask(e)}
onDragLeave={(e) => onDragLeaveTask(e)}
onDrop={(e) => onDropTask(e)}
>
{taskTodo.map((currentTask, index) => {
return (
<Card
variant='outlined'
className={classes.cardTask}
key={index}
style={{ background: '#f3f3f3', marginTop: '1rem' }}
onDragStart={(e) => onDragStartTask(e, currentTask._id)}
draggable
>
<div className={classes.topContent}>
<CardActions
className={
classes[
currentTask.priority === 'High'
? 'checkBoxContainerHigh'
: currentTask.priority === 'Minor'
? 'checkBoxContainerMinor'
: currentTask.priority === 'Low'
? 'checkBoxContainerLow'
: 'checkBoxContainer'
]
}
>
<Checkbox
icon={<CircleUnchecked />}
checkedIcon={<CircleCheckedFilled />}
color='primary'
className={classes.completeAction}
id={currentTask._id}
checked={currentTask.isComplete}
onChange={handleCheckStatus}
/>
</CardActions>
<CardContent className={classes.cardContentTop}>
<Typography variant='h6'>{currentTask.title}</Typography>
<Typography variant='caption' color='textSecondary'>
<EventIcon />
{moment(currentTask.dateDue).format('dddd, Do MMMM')}
</Typography>
</CardContent>
{currentTask.desc && (
<CardActions disableSpacing style={{ marginLeft: 'auto' }}>
<IconButton
className={clsx(classes.expand, {
[classes.expandOpen]: expanded,
})}
onClick={handleChange(`todo-panel_${index}`)}
>
<ExpandMoreIcon />
</IconButton>
</CardActions>
)}
</div>
{currentTask.desc && (
<Collapse
in={expanded === `todo-panel_${index}`}
timeout='auto'
unmountOnExit
className={classes.collapsePanel}
color='primary'
>
<CardContent className={classes.descPrevContainer}>
<Typography
variant='body1'
className={classes.text}
dangerouslySetInnerHTML={createMarkup(
textTruncate(currentTask.desc, 50)
)}
></Typography>
</CardContent>
</Collapse>
)}
<CardActions
disableSpacing
className={classes.bottomActionsContainer}
>
<Tooltip title='Delete'>
<IconButton
aria-label='delete'
className={classes.BottomDelete}
taskid={currentTask._id}
onClick={handleDelete}
>
<DeleteIcon />
</IconButton>
</Tooltip>
<Tooltip title='Edit'>
<IconButton
aria-label='edit'
className={classes.BottomEdit}
taskid={currentTask._id}
>
<EditIcon />
</IconButton>
</Tooltip>
</CardActions>
</Card>
);
})}
</Container>
</Container>
</>
);
};
As a previous commenter mentioned, pass a callback down to the Child. Update your parent component as follows
const Home = ({ history }) => {
const [taskDeleteId, setDaskDeleteId] = useState();
const [showPrompt, setShowPrompt] = useState(false);
return (
<>
<Grid>
<TasksList deleteId={taskDeleteId} setDeleteId={setDaskDeleteId} />
<PromptComponent showPrompt={showPrompt} taskId={taskIdref} />
</Grid>
</>
);
};
Then in your child you can call the function passed from the parent to set the state properly.
const TasksList = ({ taskId, deleteId, setDeleteId }) => {
// Function
const handleDelete = (e) => {
setDeleteId(e.currentTarget.getAttribute('taskId'));
...
};
return (...

How to get the key on click event

I have the following react component:
type MobileNavProp = RouteTableProp
export default function MobileNav({routes}: MobileNavProp) {
const classes = useStyles()
const [drawerOpen, setDrawerOpen] = useState<boolean>(false)
const handleOnClickItem = (event: React.MouseEvent<HTMLDivElement>): void => console.log(event.target)
return (
<React.Fragment>
<IconButton
className={classes.icon}
color="inherit"
aria-label="Open navigation"
edge="end"
onClick={() => setDrawerOpen(true)}
>
<MenuRoundedIcon fontSize="large"/>
</IconButton>
<SwipeableDrawer open={drawerOpen}
onClose={() => setDrawerOpen(false)}
onOpen={() => console.log("Drawer is open")}
disableBackdropTransition={!iOS}
anchor="top"
disableDiscovery={iOS}
>
<List subheader={
<ListSubheader component="div" id="logo" className={classes.company}>
<img className={classes.logo} src={logo} alt="databaker-logo"/>
<Typography variant="h6" className={classes.title}>
DATABAKER
</Typography>
</ListSubheader>
}>
{
routes.map((rt, index) => (
<ListItem
divider
key={index}
button
onClick={handleOnClickItem}
>
<ListItemText primary={rt.name}/>
</ListItem>
))
}
</List>
</SwipeableDrawer>
</React.Fragment>
)
}
When the user click on ListItem(the handler function is handleOnClickItem), then it shows me:
<span class="MuiTypography-root MuiListItemText-primary MuiTypography-body1 MuiTypography-displayBlock">ABOUT US</span>
However, I would like to get the clicked index that I have provided:
<ListItem
divider
key={index}...
How to get it?
You can make handleOnClickItem a higher-order function that takes the index as a parameter, and call it:
const makeHandleOnClickItem = (i: number) => (event: React.MouseEvent<HTMLDivElement>) => {
console.log(event.target);
console.log(i);
};
and change
onClick={handleOnClickItem}
to
onClick={makeHandleOnClickItem(index)}

categories and sub categories in ReactJS

Hello I'm trying to make a sidebar menu using material ui
This sidebar menu contain categories and there sub categories.
I have code this:
What I'm trying to make ?
I take some categories data from my Laravel API, on each categories they can have an object of sub categories, and I collapse this to show the sub categories from the parent category
If I collapse one every one collapse, this is normal because they share the same state, but I don't know how to make one state for each parent categories
const categoriesList = this.state.categories.map((item, k) => {
return (
<React.Fragment key={k}>
<ListItem button onClick={this.handleClick}>
<ListItemText primary={item.label} />
{item.category !== null ? this.state.open ? <ExpandLess /> : <ExpandMore /> : null}
</ListItem>
{item.category !== null ? <>
<Collapse in={this.state.open} timeout="auto" unmountOnExit>
<List component="div" disablePadding>
{item.category.map((ite,l) => <ListItem key={l} button className={classes.nested}><ListItemText primary={ite.label} /></ListItem>)}
</List>
</Collapse>
</> : null}
</React.Fragment>
)
})
``
I would recommend that you create a component for each category. So instead you would have:
const categoriesList = this.state.categories.map((item, k) => {
return <Category key={k} {...item} />;
});
And elsewhere in your code you'd have another Component:
const Category = (props) => {
const [open, setOpen] = useState(false);
function handleClick(e) { ... }
return (
<React.Fragment>
<ListItem button onClick={handleClick}>
<ListItemText primary={props.label} />
{props.category !== null ? open ? <ExpandLess /> : <ExpandMore /> : null}
</ListItem>
{item.category !== null ? <>
<Collapse in={open} timeout="auto" unmountOnExit>
<List component="div" disablePadding>
{props.category.map((ite,l) => <ListItem key={l} button className={classes.nested}><ListItemText primary={ite.label} /></ListItem>)}
</List>
</Collapse>
</> : null}
</React.Fragment>
);
}
Then the state is contained with the Category component, and you can keep your container component simple.
[note] I prefer function components, so used that here, but a Class Component wouldn't change this answer.
This is my parent component with hook
first time I use hooks
import React, {useState} from 'react';
import ListItem from "#material-ui/core/ListItem";
import ListItemText from "#material-ui/core/ListItemText";
import ExpandLess from "#material-ui/icons/ExpandLess";
import ExpandMore from "#material-ui/icons/ExpandMore";
import Collapse from "#material-ui/core/Collapse";
import List from "#material-ui/core/List";
export function Category(props){
const [open, setOpen] = useState(false);
let handleClick = () => {
setOpen(!open)
}
return (
<React.Fragment key={props.id}>
<ListItem button onClick={handleClick}>
<ListItemText primary={props.categorie.label} />
{props.category !== null ? open ? <ExpandLess /> : <ExpandMore /> : null}
</ListItem>
{props.category !== null ? <>
<Collapse in={open} timeout="auto" unmountOnExit>
<List component="div" disablePadding>
{props.category.map((ite,l) => <ListItem key={l} button className={props.classes.nested}><ListItemText primary={ite.label} /></ListItem>)}
</List>
</Collapse>
</> : null}
</React.Fragment>
)
}

Loading React component from another file

I am using the React Material-UI library. I have a search 'filter' on the main/parent page, which should pop open a Drawer that is located in a separate file.
I understand how this all works within one file, but I cannot understand how to break this into separate files (obviously for code clarity intent). I also cannot tell how I get the 'variables' out of the Drawer for items I select. Below I have pasted my two files, whereby I would like to get the 'onclick' in the parent to launch the Drawer in the child file. Any assistance would be excellent!
My parent file:
return (
<Autocomplete
id="search"
...
renderInput={(params) => (
<TextField
{...params}
label="Search for an item"
variant="outlined"
InputProps={{
...params.InputProps,
startAdornment: (
<InputAdornment position="start">
<IconButton color="primary" aria-label="filters" component="span">
<TuneRoundedIcon onClick={ handleFilterOpen } />
</IconButton>
<Filter open={ openFilter} />
</InputAdornment>
),
endAdornment: (
<React.Fragment>
{loading ? <CircularProgress color="inherit" size={20} /> : null}
{params.InputProps.endAdornment}
</React.Fragment>
),
}}
fullWidth
/>
)}
And this is the Filter.tsx (my child file):
export default function Filter() {
const classes = useStyles();
const theme = useTheme();
const [open, setOpen] = React.useState(false);
const handleDrawerOpen = () => {
setOpen(true);
};
const handleDrawerClose = () => {
setOpen(false);
};
return (
<Drawer
className={classes.drawer}
variant="persistent"
anchor="right"
open={open}
classes={{
paper: classes.drawerPaper,
}}
>
<div className={classes.drawerHeader}>
<IconButton onClick={handleDrawerClose}>
{theme.direction === 'rtl' ? <ChevronLeftIcon /> : <ChevronRightIcon />}
</IconButton>
</div>
<Divider />
<List>
{['Inbox', 'Starred', 'Send email', 'Drafts'].map((text, index) => (
<ListItem button key={text}>
<ListItemIcon>{index % 2 === 0 ? <InboxIcon /> : <MailIcon />}</ListItemIcon>
<ListItemText primary={text} />
</ListItem>
))}
</List>
<Divider />
<List>
{['All mail', 'Trash', 'Spam'].map((text, index) => (
<ListItem button key={text}>
<ListItemIcon>{index % 2 === 0 ? <InboxIcon /> : <MailIcon />}</ListItemIcon>
<ListItemText primary={text} />
</ListItem>
))}
</List>
</Drawer>
);
}
You need to pass the open state down to the child component via props. The child component should not have an "open" state. I've simplified this for you for better understanding:
const Parent = () => {
const [open, setOpen] = React.useState(false);
const toggleOpen = () => {
setOpen(!open)
}
const customFunction(valueFromChildComponent) {
alert(valueFromChildComponent); // this alerts "myValue" since "myValue" is passed from child component
}
return (
<Child isOpen={open} myCustomFunction={customFunction} />
);
}
Child component
const Child = props => {
return (
console.log(props.isOpen) // props.isOpen contains the value passed down from the parent
<button onClick={props.myCustomFunction("myValue")}>test Button</button>
);
}
I've also added an example how you could pass up a value form your child component. in my case i passed up a string but you could for example pass up the selected value of a drop down or basically whatever you want.

Resources