React 18 useState resets array in functional component - javascript

I have a series of checkboxes that need to push their name into a state array when checked, and remove their name when they are unchecked. No matter what I do, checking any sets the array to contain only that checkbox's name.
const [selectedItems, setSelectedItems] = useState([]);
useEffect(()=> {
// Log selectedItems out every time it changes.
console.log(selectedItems) // Always only the last selected checkbox name, no others.
}, [selectedItems])
const onCheckChange = e=> {
console.log(e.target.name, e.target.checked) // Are as expected, the name and value.
if (e.target.checked) {
setSelectedItems(prev=> [...prev, e.target.name])
// setSelectedItems([...selectedItems, e.target.name]) // Same result.
} else {
setSelectedItems( prev=> prev.filter(s=> s!==e.target.name) )
// setSelectedItems(selectedItems.filter(s=> s!==e.target.name)) // Same result.
}
}
Every time I check a checkbox, selectedItems is set to contain only the selected item's name. Every time I uncheck a checkbox, setSelectedItems is set to [].
This seems like the most normal pattern ever, but there must be some weirdness with arrays and useState? Thanks to anyone who can help!

The problem is in e.target.name. Most likely all your checkboxes are using the same name.
To fix, try to give different names, or use something else to find which checkbox was interacted (for example use index, with adding it as an argument of your onChange handler)

Related

Vue trying to filter an array but I get no results

I have a Vue 3 application
I have an object of posts. Each post has a different category. At the top of the page I have check boxes that I can check to filter the array and only show the categories I added to another array called visibleList. If I click the check box for cars then 'cars' is added to visibleList.
I need to show only posts with the category of cars if I check the cars checkbox. Now if I check the box I see no posts
I created an extra object that I can use to filter the objects. That object is populated when I load the app.
When I check a box the posts disappear. In the console I can see the number of posts with the correct category showing true and the others showing false.
Here is my function for sorting the posts:
function filterPosts() {
filtered.value = posts.value;
if (visibleList.value.length != 0) {
filtered.value = filtered.value.filter((e) => {
visibleList.value.includes(e.category);
console.log(visibleList.value.includes(e.category));
});
} else {
filtered.value = posts.value;
}
}
What am I doing wrong that I cannot see the posts from the selected category?
Looks like you are not returning a value from the filter function. The filter function expects a boolean value to determine whether an element should be included in the filtered array or not.
filtered.value = filtered.value.filter((e) => {
return visibleList.value.includes(e.category);
});
Just an Alternative!!
Since you are using the arrow function, you can use this approach to filter down the value.
filtered.value = filtered.value.filter((e) => visibleList.value.includes(e.category));

How to use a index value of a dynamic array (which get filled by a click event) for filtering a new array

I'm working on a filter, which filters a array of nested arrays down to the value of one last index.
This happens in 5 steps. At each step you choose which index value (string) get used to filter the array further.
Example: You have 5 categories, each have 6 themes. Each of these 6 themes has 6 focusses(sub themes). Each focus has 6 questions. Each question has 1 answer. First you pick a categorie. This narrows the arrays down to all arrays with that categorie. Then a theme, which narrows the array down to only the arrays with that theme... etc...
So far I managed to filter down to the right question.
You can find the code here: https://github.com/okestens/vertical-filter.git
To get this work, I hardcoded the string "Deskundigheid" as a condition for the equality operator (===) that get used for the filter.
Example:
// FILTER QUESTIONS // I tried to use state.focus[0] but it does not work
let unique_questionsA = [. // now this is hardcoded
...new Set(formsA.filter((i) => i[2] === "Deskundigheid").map((i) => i[3])),
]; --------------
// FUNCTION
function displayQuestionsA() {
state.questions = [];
unique_questionsA.forEach(function (question, index) {
document.getElementById("question" + index).innerHTML = question;
state.questions.push(question);
});
------
// the state object
let state = {
category: [],
themes: [],
focus: [],
question: [],
answer: [],
};
But. What I want this filter to use is not a hardcoded string (deskundigheid) but the exact string that is visible in the div (coming from a click event which creates this filtered array and get stored in the state object). See image.
I thought: I need to track these arrays (with an object called 'state', capturing these dynamic arrays). If I then want to filter the right questions, by using the value (string) of the chosen focus (For example 'Deskundigheid', which is visible to the user), I just refer to the corresponding index value (state.focus[0]) of that chosen focus string, coming from the dynamic state object.
But, if I use the index state.focus[0] for the filter which creates the questions array, I get an empty array :(
My thought: Although the empty focus array (inside the state object), which get filled by a click event, eventually is filled with the right strings, the filter for the new array (unique_questionsA), which uses 'state.focus[0]' does not read the filled array as ‘filled’ but as empty.
I have not idea why :(
I hope I'm clear. If so, and you maybe have a clue, I would love to have a chat! Thanks O
The question can be summed up to
how do I get the text of the element when clicked, in an onclick event
listener callback function.
Your focusScript.js can be modified to
function displayQuestionsA(e) {
state.questions = [];
let unique_questionsA = [...new Set(formsA.filter((i) => i[2] === e.target.innerText).map((i) => i[3]))];
}
document.querySelector(".focus__A").addEventListener("click", displayQuestionsA);
Notice the e.target.innerText which contains the text inside the element that triggered the event(which you clicked).
if I got you correctly - both map and filter functions can give your callback second parameter - the index.
arr.map((n,i)=>{console.log(`[${i}]:${n}`)})

Why am I getting undefined while trying to filter the movie release_date?

This is the favorites state which contains the movie release_date.
I'm trying to put filter the release_date into the dates state as shown here:
sortMovies= () => {
let releases = this.state.favorites.filter(this.favorites.release_date)
this.setState({
dates: releases,
});
console.log(this.state.dates);
}
I put this function in a button onClick and when I press it I get :
How do I filter out all the release_date in the favorites state and add it to the dates state?
filter will filter out each entry in the array that returns a truthy value. You are trying to extract the release_date of each favorite, so map would be more suited for this:
let releases = this.state.favorites.map(favorite => favorite.release_date);
Because Array.prototype.filter() expects the first parameter to be a
function
You are trying to use this.favorites.release_date which is not a function and it is undefined because this.favorites is an array.
Instead use a callback function which will test each element of the array and will return true in the case of match, otherwise false.
const releases = this.state.favorites.filter(movie => !!movie.release_date)

WebdriverIO loop through element list

So I have a table of 250 of rows, and I want to just get all the values from one column and check if they meet the required criteria:
const rows = browser.elements(selector..);
const numbers = [];
rows.value.forEach(cellData => {
const value = browser.elementIdText(cellData.value.ELEMENT).value;
// some logic to check if the value is ok
numbers.push(value);
});
// check if all numbers are sorted correctly
, but it most of the time it fails on the line (it says stale element reference: element is not attached to the page document):
const value = browser.elementIdText(cellData.value.ELEMENT).value;
I tried doing cellDate.getText(), but there was a Java socket error, could someone help? I assume the selector is not attached to the page as indicated, but I can't figure my head out how to just loop through them all.
I had a solution similar to your method before and while it seems to work, I think there might just be some slight adjustments to your code to get what you want. I never had much luck chaining from the end of the elementIdText call.
Step 1: Grab all the Data (browser.elements or browser.$$):
let cellData = browser.$$('selector that matches desired Column Data')
The above returns an array of JSON WebElements. And as you know you can correctly loop through the array looking at the "values". If you use the selector that matches the Column Values you're looking for you should have all similar data stored in the element.value.ELEMENT.
Step 2: Loop through the cellData array and pluck out the text values of the ELEMENT using browser.elementIdText()
cellData.forEach((elem) => {
let number = browser.elementIdText(elem.value.ELEMENT)
//elementIdText also returns a JSON WebElement so it's number.value
if(number.value === <condition>) {
console.log('number looks good')
//perform other on value logic
}
})
//perform other logic still in loop EX: array.push()
})
I hope this helps! Let me know if you hit any snags!

Observable array and computed error

I have a list of checkboxes that execute functions on check and uncheck. I also have an observable array that holds the values of the checkboxes currently active (put into local storage). The relevant code is here:
this.layerToggleChecked = knockout.observableArray();
// ...
this.layerToggle = function (source, name, type, url, description) {
return knockout.computed({
read: function () {
return this.layerToggleChecked();
},
write: function (checked) {
if (checked) {
alert("loading");
this.layerToggleChecked.push(source());
} else {
alert("removing");
this.layerToggleChecked.remove(source());
}
}
}, this);
}
The checkboxes work as planned triggering the functions until I added return this.layerToggleChecked(); which returns
knockout-3.2.0.js:13 Uncaught TypeError: b.push is not a function
Knockout's checked binding handles arrays differently from other values. You're expecting a true or false to be written to the computed observable, but you're returning an array, which obviously aren't the same.
From http://knockoutjs.com/documentation/checked-binding.html:
Special consideration is given if your parameter resolves to an array. In this case, KO will set the element to be checked if the value matches an item in the array, and unchecked if it is not contained in the array.
When the user checks or unchecks the checkbox, KO will add or remove the value from the array accordingly.
Since you're using an array to hold the checked values, you're better off binding directly to it:
<input type="checkbox" data-bind="checkedValue: source, checked: layerToggleChecked" />

Categories

Resources