'splice' instead of 'filter' - javascript

I have a field for entering tags. It is also possible to remove tags.
And here I have a question.
My deletion code is below. Tags can be deleted one at a time, in any order (first, last, somewhere in the middle, it doesn't matter).
const deleteTag = (index) => {
setTags((prevState) => prevState.filter((tag, i) => i !== index));
};
But I would like to use 'splice' instead of 'filter'. With the same functionality.
Tell me how to do it

Splice's first argument is the item index, and the second argument is how many items you want to delete from that index.
const deleteTag = (index) => {
setTags((prevState) => {
prevState.splice(index, 1)
return [...prevState]
});
};
UPDATE
If you are using <StrictMode> the setState will execute twice and will delete two items instead of one.

Related

filtering out an array of items setting state

I am looking to filter through an array and return all elements of the array except the element which has been clicked on, so I have a map of list elements each with a key={index} of their map, onClick it should call my remove function, and pass in the index of the element to be removed, I then need to filter over that array, remove the element, update state, and send this information to my backend.
here is the delete function
const deleteItem = (id) => {
// use filter, to loop through all pieces of index
const element = list.todoItems.indexOf(id - 1);
setList({ todoItems: list.todoItems.filter(element !== id) });
console.log(list.todoItems);
dispatch(updateTodo(list));
};
here is the mapped array
{list.todoItems.map((Item, index) => (
<div
// setup anonymous function, that will call
// ONLY when the div
// is clicked on.
key={index}
onClick={() => deleteItem(index)}
>
{/* list item, gets text from props */}
<li>{Item}</li>
</div>
))}
I must be missing something, because this should work, Though i may have to shift gears and have each item as an actual object in my database, though id rather not do this as i feel an array of strings is more than appropriate for this app.
Remove your indexOf logic and this will work.
You don't have to find the index of the array because you're already receiving it as a parameter.
const deleteItem = (id) => {
setList({ todoItems: list.todoItems.filter((_, filterID) => filterID !== id) });
console.log(list.todoItems);
dispatch(updateTodo(list));
};
You don't need to subtract one from the index on the indexOf function
And for this case, splice works better than filter
const deleteItem = (id) => {
const element = list.todoItems.indexOf(id);
setList({ todoItems: {...list}.todoItems.splice(element, 1)});
dispatch(updateTodo(list));
};

React: useState array not updating

When you push an array, data is pushed. However if you check in console.log, data is not imported. It seems to be a delay. Can you tell me why is this happening?
is there solution for this?
Expected console.log result showing input, however empty array shows and if you click checkbox again then input appears.
const [checked, setChecked] = useState<number[]>([])
const handleAddListToArray = (id: number) => {
console.log(checked)
if (setChecked.includes(id)) {
setChecked(checked.filter((item) => item !== id))
} else {
setChecked([...checked, id])
}
}
--- checkbox compornent ---
const [isChecked, setIsChecked] = useState(false)
const handleChange = () => {
setIsChecked(!isChecked)
handleAddListToArray(id)
}
<Checkbox checked={isChecked} onClick={() => handleChange()} />
when you push an array, data is pushed however if you check in
console.log data is not inported. it seems to be a delay can you tell
me why is this happening?
The state-setting functions are asynchronous. In other words, if you wrote:
const [foo, setFoo] = useState(0);
setFoo(1);
console.log(foo); // logs 0, NOT 1
you would see 0 logged, not 1 ... at least initially. However, there'd be a log entry below that would show 1.
This is because set* function don't change the value in the function, but they do cause the component to be re-rendered after, which means the function is run again, and now uses the correct value..
however empty array shows and if you click checkbox again then input
appears.
It's because of this code:
setIsChecked(!isChecked)
Initially you set isChecked to an array:
setChecked(checked.filter((item) => item !== id))
But then you changed it to !isChecked ... a boolean. Once you change it to a boolean, you can't change it back to an array.
You check the setState-function if it includes the input, on your fourth row of code:
if (setChecked.includes(id))
I believe you want to chech the checked-state instead, like so:
if (checked.includes(id))
Also, consider using the useState-callback when you mutate your state based on the previous one. So instead of:
setChecked(checked.filter((item) => item !== id))
try:
setChecked((prevState) => prevState.filter((item) => item !== id))
You can also use this when you setting your isChecked state. This ensure that you get your latest known state and avoids getting in some wierd states because React updates the state asynchronous.
Some suggestions
if (setChecked.includes(id)) {
should be (setChecked is a function)
if (checked.includes(id)) {
For setChecked(checked.filter((item) => item !== id))
better usage will be
setChecked(prevCheckedValues => prevCheckedValues.filter((item) => item !== id));
This way you will always get the current checked values when you do a setChecked.
Another way to use this is via a useCallback and pass the dependency array as [checked]
If you want to print checked values each time its changed you can use a useEffect (docs) with correct dependency array.
Usage example
useEffect(()=>{
console.log("Checked", checked);
}, [checked])

React hooks removing item from an array and add it to another array

export default function ShoppingCart() {
const classes = useStyle();
const {
productsList, filteredProductsList, setFilteredProductsList, setProductsList,
} = useContext(productsContext);
const [awaitingPaymentList, setAwaitingPaymentList] = useState([]);
const [addedToCartList, setAddedToCartList] = useState([]);
const addToCartHandler = useCallback((itemId) => {
const awaitingPaymentListIds = awaitingPaymentList.map((item) => item.id);
const isInAwaitingPaymentList = awaitingPaymentListIds.includes(itemId);
isInAwaitingPaymentList ? setAddedToCartList([...addedToCartList, addedToCartList.push(awaitingPaymentList[awaitingPaymentList.findIndex((item) => item.id === itemId)])]) : setAddedToCartList([...addedToCartList]);
isInAwaitingPaymentList
? setAwaitingPaymentList(awaitingPaymentList.splice(awaitingPaymentList.findIndex((item) => item.id === itemId), 1))
: setAwaitingPaymentList([...awaitingPaymentList ])
setProductsList(awaitingPaymentList);
}, [addedToCartList, awaitingPaymentList, setProductsList]);
useEffect(() => {
setFilteredProductsList(
productsList.filter((product) => product.status === 'AWAITING_PAYMENT'),
);
}, [productsList, setFilteredProductsList, setFilteredProductsList.length]);
useEffect(() => {
setAwaitingPaymentList(filteredProductsList);
}, [filteredProductsList]);
I manage to delete the item from awaitingPaymentList and to add it into addedToCartList but looks like I am doing something wrong because it is adding the object, but the previous ones are replaced with numbers :). On the first click, the array is with one object inside with all data, but after each followed click is something like this => [1,2,3, {}].
When I console log addedToCartList outside addToCartHandler function it is showing an array: [1] :)))
Since there is some code I hope I am not going to receive a lot of negative comments like last time. And if it's possible, to give me a clue how to make it for all items to be transferred at once, because there will be a button to add all. Thank you for your time.
I think this line of code is causing issue:
isInAwaitingPaymentList
? setAddedToCartList([
...addedToCartList,
addedToCartList.push(
awaitingPaymentList[
awaitingPaymentList.findIndex((item) => item.id === itemId)
]
)
])
: setAddedToCartList([...addedToCartList]);
array.prototype.push returns the new length of the array that you are pushing into, this is likely where the incrementing element values are coming from. The push is also a state mutation.
It is not really clear what you want this code to do, but I think the push is unnecessary. Perhaps you meant to just append the last element into the new array you are building.
isInAwaitingPaymentList
? setAddedToCartList([
...addedToCartList, // <-- copy state
awaitingPaymentList[ // <-- add new element at end
awaitingPaymentList.findIndex((item) => item.id === itemId)
]
])
: setAddedToCartList([...addedToCartList]);
Suggestion
If you simply want to move an element from one array to another then find it in the first, then filter it from the first, and copy to the second if it was found & filtered.
const itemToMove = awaitingPaymentList.find(item => item.id === itemId);
setAwaitingPaymentList(list => list.filter(item => item.id !== itemId));
itemToMove && setAddedToCartList(list => [...list, { ...itemToMove }])

slice happens in the code but it doesnt update in the UI

there are three drop down menus in the initial state.
after I select the first drop down the second drop down values gets loaded
after I select the second drop down values.
a new set of drop down loads.
when I select remove button of the second set.
it doesnt remove that set but it removes the first set.
when I debugged removeSelectedValue method there slices are happening correctly but its not updating in the updating
can you tell me how to pass the queryComponents values so that it will update in the UI.
can you tell me how to fix it.
so that in future I will fix it myself.
providing my relevant code snippet and sandbox below.
all my code is in demo.js
https://codesandbox.io/s/4x9lw9qrmx
removeSelectedValue = index => {
console.log("removeSelectedValue--->", index);
let seletedValues = this.state.queryComponents;
seletedValues.splice(index, 1);
console.log("spliced Values--->", seletedValues);
this.setState({ queryComponents: seletedValues });
};
render() {
let queryComp = this.state.queryComponents.map((value, index) => {
return (
<AutoCompleteComponent
key={index}
value={value}
index={index}
valueSelected={this.getSelectedValue}
removeSeleted={this.removeSelectedValue}
/>
);
});
return <div>{queryComp}</div>;
}
When you do let seletedValues = this.state.queryComponents;
you're creating a reference to that variable, instead of making a copy.
You need to make sure you replace your state with a new object/array for the re-render to happen.
Please try this:
removeSelectedValue = index => {
this.setState(prevState => ({
queryComponents: prevState.seletedValues.filter((a, i) => (i !== index));
});
};
That filter function is equivalent to the splice you were using, but returns a new array instead of modifying the original one.
On the other hand, I 'm passing setState a function that uses prevState making the code shorter.

Splice removes only last element in array

I'm having a small issue with React (still new to it). I have an array of Results. These Results have nested Bookings, also in an array, and the latter is what I'm manipulating.
When User creates Booking, everything goes as expected - findIndex gets the correct Result element and modifies its Bookings array accordingly.
However, when I want to "Unbook", it only finds the last Result in the array, and findIndex is always -1 (so I haven't even gotten to the Bookings part, because the Result index I get is wrong).
The code is similar, my items all have unique keys, and I don't understand what could be the problem (using Alt as Flux implementation)?
Here is what happens on Create:
onCreateBookingSuccess(data) {
let resultIndex = this.results.findIndex((result) => result.id === data.id);
this.results.update(resultIndex, (result) => result.bookings.push(data));
toastr.info('Booked! User will receive notification.');
}
And on delete:
onDestroyBookingSuccess(data) {
let resultIndex = this.results.findIndex((result) => result.id === data.id);
var myBooking;
this.results.map((result) => {
myBooking = result.bookings.findIndex((booking) => booking.id === data.booking);
});
this.results.update(resultIndex, (result) => result.bookings.splice(myBooking,1));
toastr.warning('Unbooked! User will receive notification.');
}
My object:
<Result key={result.id} id={result.id} bookings={result.bookings} />
As I mentioned, the first operation goes as planned, everything is modified as it should. The issue with the second op starts from the very beginning, when resultIndex returns -1.
The problem seems to be here:
var myBooking;
this.results.map((result) => {
myBooking = result.bookings.findIndex((booking) => booking.id === data.booking);
});
You’re always assigning to myBooking, even when the index is not found (-1) after having already found it, so it’s equivalent to this.results.last().bookings.findIndex(...). Really you only want to get the (first?) value that’s not -1:
var myBooking = this.results.map((result) => {
myBooking = result.bookings.findIndex((booking) => booking.id === data.booking);
}).find((index) => index != -1);
Also, consider renaming myBooking to better indicate it’s an index and not the actual record.

Categories

Resources