React Material UI Delete Selected Cells - reactjs

I am using a table example from material ui.
This is the link to the online project https://codesandbox.io/s/209kpmpvx0
The example allows you to cell multiple rows and a delete icon gets displayed when you click on the row.
I would like to be able to print out all the selected rows after the delete icon has been pressed.
This is the class that allows you to select a row.
class EnhancedTable extends React.Component {
constructor(props) {
super(props);
this.state = {
selected: [],
}
handleClick = (event, id) => {
const { selected } = this.state;
const selectedIndex = selected.indexOf(id);
let newSelected = [];
this.setState({ selected: newSelected });
};
I put the following code selectedId={selected.id} into the EnhancedTableHead component.
return (
<Paper className={classes.root}>
<EnhancedTableToolbar numSelected={selected.length} />
<div className={classes.tableWrapper}>
<Table className={classes.table} aria-labelledby="tableTitle">
<EnhancedTableHead
numSelected={selected.length}
selectedId={selected.id}
order={order}
orderBy={orderBy}
onSelectAllClick={this.handleSelectAllClick}
onRequestSort={this.handleRequestSort}
rowCount={data.length}
/>
Then I added the selectedId to the EnhancedTableToolbar. I also added an event handler into the delete icon.
let EnhancedTableToolbar = props => {
const { numSelected, classes } = props;
return (
<Toolbar
className={classNames(classes.root, {
[classes.highlight]: numSelected > 0
})}
>
<div className={classes.title}>
{numSelected > 0 ? (
<Typography color="inherit" variant="subheading">
{numSelected} selected
</Typography>
) : (
<Typography variant="title" id="tableTitle">
Nutrition
</Typography>
)}
</div>
<div className={classes.spacer} />
<div className={classes.actions}>
{numSelected > 0 ? (
<Tooltip title="Delete">
<IconButton aria-label="Delete">
<DeleteIcon onClick={printdeletearray} />
</IconButton>
</Tooltip>
) : (
<Tooltip title="Filter list">
<IconButton aria-label="Filter list">
<FilterListIcon />
</IconButton>
</Tooltip>
)}
</div>
</Toolbar>
);
};
I get an error saying selectedId is undefined.
const printdeletearray = () => {
console.log(this.state.selected);
};

You made two mistakes I guess:
firstly you should pass selected as selectedId props in <EnhancedTableToolbar />:
<EnhancedTableToolbar
numSelected={selected.length}
selectedId={selected}
/>
the second mistake is that you should pass selectedId to printdeletearray() like this:
<DeleteIcon onClick={() => printdeletearray(selectedId)} />
and here is your Demo

Related

How can I add item on localStroge through ReactJS

I don't understand how I can add an item on localStroge with the handle change method in react js.
The problem is I want to add a favorite list. When I click the favorite list all checkbox fill I need to control them.
I want to store the filled item in my local storage. handlechange function fill all favorite icon why?
Every click will be a single item fill. Is it possible to Material UI or Martial UI checkBox icon? How can I handle it?
Here is my UI view
function Main() {
// Here is my state define
const [checked, setChecked] = React.useState(
localStorage.getItem("love") === "true"
);
const handleChange = (e) => {
localStorage.setItem("love", `${e.target.checked}`);
setChecked(e.target.checked);
console.log(e.target.checked);
};
return (
<>
<div className="row mt-5">
{isLoading ? (
<>
{Array.from({ length }).map((_, i) => (
<MainLoading key={i} />
))}
</>
) : error ? (
<p>Error occured</p>
) : (
<>
{data?.map((product) => (
<div className="col-md-3 mb-5 text-center" key={product.id}>
<img
className="w-100"
style={{ height: "200px", objectFit: "contain" }}
src={product.image}
alt=""
/>
<div>{product.title.substring(0, 20)}</div>
<button
onClick={() => handelAddTocard(product)}
className="mt-3"
>
Add to card
</button>
<button>
<Link
to={`/details/${product.id}`}
className="mt-3 text-decoration-none text-black"
>
view details
</Link>
</button>
{/* How can i control evey single item */}
<Checkbox
checked={checked}
onChange={handleChange}
icon={<FavoriteBorder />}
checkedIcon={<Favorite />}
/>
</div>
))}
</>
)}
</div>
</>
);
}
export default Main;
The problem is that you are using a boolean and you have no way to identify a specific item.
If you want to favorite multiple items, I would use something like this:
const [checked, setChecked] = React.useState(
JSON.parse(localStorage.getItem("loveIds") || "[]")
);
const handleCheck = (id, productChecked) => {
const newItems = productChecked ? [...checked, id] : checked.filter(x => x !== id);
localStorage.setItem("loveIds", JSON.stringify(newItemS));
setChecked(newItems);
console.log(newItems);
};
// ...
<Checkbox
checked={checked}
onChange={(e) => handleCheck(product.id, e.target.checked)}
icon={<FavoriteBorder />}
checkedIcon={<Favorite />}
/>

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 (...

Passing function to another component getting error this is not defined react js

I am developing an app with sliding menu in react but I am getting the following error.
this is not defined
My code
Myfile
const toggleMenu = () => {
alert("clicked");
state = !state
if (state === false) {
const visibllity = "close";
alert("open")
} else {
const viisibility = "open"
alert("close")
}
}
return (
<RiderHeader toggleMenu={this.toggleMenu()} />
)
RiderHeader.js
return (
<div className="user-header">
<IconButton onClick={() => toggleMenu()}>
<MenuIcon />
</IconButton>
</div>
)
thisYou shouldn't call the function when you are declaring (or passing it as a prop).
<Component prop={this.func} /> or <Component prop={() => this.func()} />
In you file where toggleMenu is defined,
return (
<RiderHeader toggleMenu={this.toggleMenu} />
)
In RiderHeader.js,
If it's a class component then,
return (
<div className="user-header">
<IconButton onClick={() => this.props.toggleMenu()}>
{/* or
<IconButton onClick={this.props.toggleMenu}>
*/}
<MenuIcon />
</IconButton>
</div>
)
If it's a functional component,
return (
<div className="user-header">
<IconButton onClick={() => props.toggleMenu()}>
{/* or
<IconButton onClick={props.toggleMenu}>
*/}
<MenuIcon />
</IconButton>
</div>
)

Slide Material-UI cannot read property style of "undefined"

I have implemented Material-UI Slide in my project recently and wanted to ask if someone could explain to me why the code works when I write it this way:
{selectedItem && selectedItem.modal && selectedItem.modal.body ? (
selectedItem.modal.body.map((section, key) => (
<Section section={section} key={key} />
))
) : (
<Slide
direction={animate === 'stepIn' ? 'right' : 'left'}
in={animate === 'idle'}
>
<Grid container={true} spacing={3}>
{items.map((item, key) => (
<Grid item={true} xs={6} md={4} lg={3} key={key}>
<MaterialCard
key={key}
onClick={onCardClicked(item)}
className={classes.card}
>
<CardActionArea className={classes.cardArea}>
<CardMedia
image={item.image || undefined}
component="img"
/>
<CardContent className={classes.cardContent}>
<Typography
component="p"
className={classes.cardContentTypographyHeader}
>
<Hyphenated language={de}>{item.label}</Hyphenated>
</Typography>
{item.description ? (
<Typography
component="p"
className={classes.cardContentTypography}
>
<Hyphenated language={de}>
{item.description}
</Hyphenated>
</Typography>
) : null}
</CardContent>
</CardActionArea>
{selectedItem && selectedItem.id === item.id ? (
<>
<div className={classes.cardSelectedOverlay} />
<Done className={classes.cardSelectedOverlayIcon} />
</>
) : null}
</MaterialCard>
</Grid>
))}
</Grid>
</Slide>
But fails to compile when I move the section.map inside the slide. I want to animate the section coming in aswell.
<Slide
direction={animate === 'stepIn' ? 'right' : 'left'}
in={animate === 'idle'}
>
{selectedItem && selectedItem.modal && selectedItem.modal.body ? (
selectedItem.modal.body.map((section, key) => (
<Section section={section} key={key} />
))
) : (
<Grid container={true} spacing={3}>
{items.map((item, key) => (
<Grid item={true} xs={6} md={4} lg={3} key={key}>
<MaterialCard
key={key}
onClick={onCardClicked(item)}
className={classes.card}
>
<CardActionArea className={classes.cardArea}>
<CardMedia
image={item.image || undefined}
component="img"
/>
<CardContent className={classes.cardContent}>
<Typography
component="p"
className={classes.cardContentTypographyHeader}
>
<Hyphenated language={de}>{item.label}</Hyphenated>
</Typography>
{item.description ? (
<Typography
component="p"
className={classes.cardContentTypography}
>
<Hyphenated language={de}>
{item.description}
</Hyphenated>
</Typography>
) : null}
</CardContent>
</CardActionArea>
{selectedItem && selectedItem.id === item.id ? (
<>
<div className={classes.cardSelectedOverlay} />
<Done className={classes.cardSelectedOverlayIcon} />
</>
) : null}
</MaterialCard>
</Grid>
))}
</Grid>
)}
</Slide>
The code works for every card, except the ones with a modal inside them. A modal contains e.g. text, textinput. When I click on a card with a modal inside of it i get this error:
Thanks for your help!
In the documentation for Slide's children prop you can find:
A single child content element.
⚠️ Needs to be able to hold a ref.
Slide uses React.cloneElement to add a ref and props to the single child. If there are multiple children or if children is an array (even if the array contains only one child), then Slide will get the error you encountered because children.props is not defined and it is trying to reference children.props.style.
Below is a little example to just help better understand the cause of the error:
import React from "react";
import ReactDOM from "react-dom";
const MockSlide = ({ children }) => {
if (children.props) {
return (
<div>
{children}
children.props is defined
</div>
);
}
return (
<div>
{children}
children.props is not defined
</div>
);
};
const sectionArray = [
"An array also causes problems (even if only one element)"
];
function App() {
return (
<div className="App">
<MockSlide>
<div>Single child works fine</div>
</MockSlide>
<br />
<br />
<MockSlide>
<div>Multiple children</div>
<div>causes problems with Slide</div>
</MockSlide>
<br />
<br />
<MockSlide>
{sectionArray.map(section => {
return <div>{section}</div>;
})}
</MockSlide>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
One potential solution is to wrap everything inside the Slide in a single <div> element.

Resources