useState remove item from array - javascript

I'm trying to remove an item from an error but it's not working like expected.
Im using state:
const [actions, setActions] = useState([
{
action: "",
key: ""
}
]);
I have a button to add actions:
<IconButton
icon="add"
bgColor="white"
iconColor="darkGray"
onClick={() =>
setActions([
...actions,
{
action: "",
key: ""
}
])
}
/>
Each row has a delete and I'm trying to use the row index to delete the item in the actions array:
<IconButton
disabled={actions.length === 1}
icon="dash"
iconColor="red"
onClick={() => {
console.log(index);
setActions(actions => {
return [...actions.splice(index, 1)];
});
}}
/>
https://codesandbox.io/s/actions-selector-n9xb4

Try using filter. It does not modify the existing array and can be used like this:
setActions(prevActions => (
// Filter out the item with the matching index
prevActions.filter((value, i) => i !== index)
));

<IconButton
disabled={actions.length === 1}
icon="dash"
iconColor="red"
onClick={() => {
setActions(actions.filter((item, i) => i !== index));
}}
/>
I tested it in your Codesandbox and it worked

Related

Passing an object Id by submit button in order to find and change value in array after submiting (react)

I want to update a value of an object nested in array after choosing new date from select button and then submiting it in order to change it.
The button (select) is nested with a rendered array object invoice in Accordion from material ui and is supposed to change a date (for now a year only) and save it while comparing its Id number.
I have two components : sideBar and simpleForm
const SideBar = ({ className }) => {
const [invoices, setInvoice] = useState([
{ label: 'Test', invoiceDate: '2021', id: 0 },
{ label: 'Test', invoiceDate: '2022', id: 1 },
{ label: 'Test', invoiceDate: '', id: 2 },
])
const addInvoiceDate = (date, invoice) => {
setInvoice(
invoices.map((x) => {
if (x.id === invoice.id)
return {
...x,
invoiceDate: date,
}
return x
})
)
}
return (
<>
<Wrapper>
<S.MainComponent>
<div>
{invoices.map((invoice) => {
return (
<Accordion>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
<div key={invoice.id}>
{invoice.label}
{invoice.invoiceDate}
</div>
</AccordionSummary>
<AccordionDetails>
<SimpleForm addInvoiceDate={addInvoiceDate} />
</AccordionDetails>
</Accordion>
)
})}
</div>
</S.MainComponent>
</Wrapper>
</>
)
}
Simple Form :
const SimpleForm = ({ addInvoiceDate }) => {
const [date, setDate] = useState('')
const handleSubmit = (e) => {
e.preventDefault()
addInvoiceDate(date)
}
function range(start, end) {
return Array(end - start + 1)
.fill()
.map((_, placeholder) => start + placeholder)
}
const Years = range(2021, 4000)
const Options = []
Years.forEach(function (element) {
Options.push({ label: element, value: element })
})
return (
<form onSubmit={handleSubmit}>
<select value={date} required onChange={(e) => setDate(e.target.value)}>
{Options.map((option) => (
<option value={option.value}>{option.label}</option>
))}
</select>
<input type='submit' value='Save' />
</form>
)
}
My problem is, i have no clue how can i succesfully pass an id number of array object to addInvoiceDate (change invoice date)in order to find it in orginal array, compare it and then submit new value. I was testing adding a new object with a new year value and it worked, but in that case i dont have to find an id of object. Its a little bit harder if i want actually find a previous one and update it through comparing id.
Any ideas how it should be done ? Probably i overlooked something or dont have enough expierience yet :)
How about this
<SimpleForm addInvoiceDate={(date) => addInvoiceDate(date, invoice)} date={invoice.invoiceDate}/> in SideBar's return
Remove the state from SimpleForm as we now have a date prop instead
<select value={date} required onChange={(e) => addInvoiceDate(e.target.value)}> in SimpleForm's return
Please leave a comment if there are questions

How to dynamically change className of a component in React?

I want to make a button check which add a new calssName to my list. I use a function to update a state and take the string. If you want to help me be more specific because I am a beginner. Thanks !
const [check, setCheck] = useState({
id: '',
status: false
});
This is the function. With 'on' I take the string to add to id.
let complet = (on) =>{
if(check.status == false){
setCheck({
id: on,
status: true
})
}
else {
setCheck({
id: on,
status: false
})
}
}
And how I Display the list and how I check correspond to the string.
return(
<div className='display'>
{ list.map( (list,index) => (
<div className={ check.status && check.id == list ? 'lista complet' : 'lista'} key= {index} id='lista' >
{list}
<button className='btnCheck' onClick={complet.bind(this, list)}> <FcCheckmark/> </button>
<button className='btnRemove' onClick={remove.bind(null, list)}> <BsTrash/> </button>
</div>
))}
</div>
)
If you want to store the checked ids and the unchecked ids, you must change your state variable because currently it can only stores a single element. However, it seems you are rendering a list of elements that can be checked individually
Here is a possible solution :
function App({list}) {
const [checkIds, setCheckIds] = useState(() => {
const item = localStorage.getItem('checkIds');
return item ? JSON.parse(item) : {};
});
// reset the checkIds when the list is updated
useEffect(() => setCheckIds({}), [list]);
// save the checkIds into the local storage
useEffect(() => {
localStorage.setItem('checkIds', JSON.stringify(checkIds));
}, [checkIds]);
function checkId(id) {
setCheckIds({...checkIds, [id]: true);
}
function uncheckId(id) {
setCheckIds({...checkIds, [id]: false);
}
return (
<div className='display'>
{list.map(id => (
<div key={id} id={id} className={check[id] ? 'lista complet' : 'lista'}>
{id}
<button className='btnCheck' onClick={() => checkId(id)}>
<FcCheckmark/>
</button>
<button className='btnRemove' onClick={() => uncheckId(id)}>
<BsTrash/>
</button>
</div>
))}
</div>
)
}

Deleting and copying array elements in React

I have been tasked to add a copy and delete button on one of our tables. I am trying to pass the index from the map to the delete and copy onclick and this does delete but...
Problem: If you copy a row it will have the exact same "i" as the original, and it moves the position of all the ones below it messing up the delete tremendously
I was under the impression that if I setRows() to something new it would run the mapping again and give them all the correct i's in each function but this doesn't seem to be the case, why?
const AdvancedTable = () => {
const [rows, setRows] = useState(tableRows); ///tableRows is basically an array of divs
const deleteOnClick = (i: number) => {
setRows(() => {
const myRows = [...rows];
myRows.splice(i, 1);
return myRows;
});
}
const copyOnClick = (i: number) => {
setRows(() => {
const myRows = [...rows];
myRows.splice(i, 0, rows[i]);
return myRows;
});
}
return (
<Paper>
{
rows.map((row: any, i: number) => (
<div>
<IconButton onClick={() => { deleteOnClick(i) }} size="small">
<ClearIcon />
</IconButton>
<IconButton onClick={() => { copyOnClick(i) }} size="small">
<FileCopyIcon />
</IconButton>
</div>
</TableCell>
{row}
))}
</Paper>
);
}
export default AdvancedTable;
Do you want the copy method to copy the row below the one you clicked or to send it to then end of the array?
maybe this can help you assuming you can call the i value from your row object with .i
const copyOnClick = (i: number) => {
setRows(() => {
const myRows = [...rows];
// send the new row to the end of the array
myRows.splice(rows.length, 0, rows[i]);
// change the new row i value to + 1 of the last object that was previously in the array
myRows[myRows.pop().i].i = rows.pop().i + 1
return myRows;
});
}

Why my check box isn't updated after click?

I'm trying to use React Hooks to update the list of items. When users click on the check-box, the app should render the items which have been selected as purchased.
I could log the event of the handPurchase() and the state is correct, however, I can't make the function render the latest state.
With class, I can do:
const handlePurchase() {
// ...
this.setState(updatedGroceries);
}
this.state.groceries.map(//...render list
This is the code:
export default function CheckboxList() {
let initialGroceries = [
{
id: "one",
text: "Apple",
purchased: false
},
{
id: "two",
text: "Banana",
purchased: false
}
];
const [groceries, setGroceries] = useState(initialGroceries);
const handlePurchase = (id: string) => () => {
groceries.forEach((item) => {
if (item.id === id) {
item.purchased = true;
}
});
setGroceries(groceries);
}
return (
<List>
{groceries.map((item) => {
const labelId = `checkbox-list-label-${item.id}`;
return (
<ListItem key={item.id} role={undefined} dense button onClick={handlePurchase(item.id)}>
<ListItemIcon>
<Checkbox
checked={item.purchased}
inputProps={{ 'aria-labelledby': labelId }}
/>
</ListItemIcon>
<ListItemText id={labelId} primary={item.text} />
</ListItem>
);
})}
</List>
);
}
Also, if I do this:
const updatedGroceries = groceries.forEach((item) => {
if (item.id === id) {
item.purchased = true;
}
});
setGroceries(updatedGroceries);
I get this error:
Argument of type 'void' is not assignable to parameter of type 'SetStateAction<{ id: string; text: string; purchased: boolean; }[]>'. TS2345
The issue is that forEach does not return anything as you might expect. I guess you would like to update with setGroceries the purchased property on the elements where the provided id is equals to the current one to true. So you need to return an array for example with Array.prototype.map(). From the documentation:
The map() method creates a new array populated with the results of calling a provided function on every element in the calling array.
I guess the following can work for you:
const updatedGroceries = groceries.map((item) => {
if (item.id === id) {
item.purchased = true;
}
return item;
});
setGroceries(updatedGroceries);
In your code you were assigning to updatedGroceries the forEach result which is undefined because it does not return anything, find here in the docs.
I hope that helps!

Custom select on react , filtering list

I'm making custom dropdown https://codesandbox.io/s/elated-driscoll-cjkcj . I want when user pick element from dropdown do not display that item in list again.
Here how it looks when I click on select first time
then when I pick that item and opening dropdown again that element shows in list
I want to remove it from that list , do not display it again. Check https://codesandbox.io/s/elated-driscoll-cjkcj for full code
<DropdownContentUI isOpen={isOpen}>
{arrayOfSvg.map((item, idx) => {
const { svg } = item;
return (
<DropdownArrowsWrapperUI key={idx} onClick={() => SelectIcon(idx)}>
{svg}
</DropdownArrowsWrapperUI>
);
})}
</DropdownContentUI>
<DropdownContentUI isOpen={isOpen}>
{arrayOfSvg.map((item, idx) => {
const { svg } = item;
if(isSelected != idx){
return (
<DropdownArrowsWrapperUI key={idx} onClick={() => SelectIcon(idx)}>
{svg}
</DropdownArrowsWrapperUI>
);
}
})}
</DropdownContentUI>
You have to filter the list of options before mapping through it.
<DropdownContentUI isOpen={isOpen}>
{arrayOfSvg
.filter((item, idx) => idx !== isSelected)
.map((item, idx) => {
const { svg } = item;
return (
<DropdownArrowsWrapperUI
key={idx}
onClick={() => SelectIcon(idx)}
>
{svg}
</DropdownArrowsWrapperUI>
);
})}
</DropdownContentUI>

Categories