Loading React component from another file - reactjs

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.

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)}

How can I use show component inside a ListItemText?

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>
}
/>

setState is not being called onClick

Working with material-ui v3.9.4, react-router v3. Created a left nav bar with a List containing Link items to redirect to another page. For some reason when I click on an item, the page is redirected, but the state is not updated until the second click to show the active item. Any idea as to why that is? Code below. I've console logged the setState change, which doesnt print until the second click.
class LeftNavigation extends Component {
constructor(props) {
super(props);
this.state = {
selectedIndex: 0,
sidebarCollapsed: false,
}
}
handleListItemClick = (event, index) => {
console.log("CLICKED"); //prints first click
this.setState({ selectedIndex: index }, () => {
console.log("SELECTED INDEX:: ", this.state.selectedIndex) //prints second click
});
}
handleCollapse = () => {
this.setState({ sidebarCollapsed: !this.state.sidebarCollapsed })
}
render() {
const { classes } = this.props;
return (
<div className={this.state.sidebarCollapsed ? classes.rootCollapsed : classes.root}>
<List component="nav">
<ListItem
button
component={props => <Link to="./pageOne" {...props} />}
selected={this.state.selectedIndex === 0}
onClick={(event) => this.handleListItemClick(event, 0)}
>
<ListItemIcon>
<CalendarIcon />
</ListItemIcon>
<ListItemText classes={{ primary: classes.listText }} primary="Page One" />
</ListItem>
<ListItem
button
onClick={(event) => this.handleListItemClick(event, 1)}
component={props => <Link to="./pageTwo" {...props} />}
selected={this.state.selectedIndex === 1}
>
<ListItemIcon>
<MailIcon />
</ListItemIcon>
<ListItemText classes={{ primary: classes.listText }} primary="Page Two" />
</ListItem>
<ListItem
button
component={props => <Link to="./pageThree" {...props} />}
selected={this.state.selectedIndex === 2}
onClick={(event) => this.handleListItemClick(event, 2)}
>
<ListItemIcon>
<NotesIcon />
</ListItemIcon>
<ListItemText classes={{ primary: classes.listText }} primary="Page Three" />
</ListItem>
</List>
<div
onClick={this.handleCollapse}>
{this.state.sidebarCollapsed ? <ChevronRight /> :
<div><ChevronLeft /><p>Collapse sidebar</p></div>}
</div>
</div>
)
}
}
export default withStyles(styles)(LeftNavigation );

Resources