How to fill a state with an array - javascript

I'm trying to add a list inside a state using the set method, but my state remains empty
App.js
// Starts the screts word game
const startGame = () => {
// pick word and pick category
const { word, category } = pickWordAndCategory();
// create array of letters
let wordLetters = word.split("");
wordLetters = wordLetters.map((l) => l.toLowerCase());
// Fill states
setPickedCategory(category);
setPickedWord(word);
setLettersList(wordLetters);
console.log('wordLetters', wordLetters);
console.log('lettersList', lettersList);
setGameState(stages[1].name);
};
const pickWordAndCategory = () => {
// pick a random category
const categories = Object.keys(words);
const category = categories[Math.floor(Math.random() * Object.keys(categories).length)];
console.log('category', category);
// pick a random word
const word = words[category][Math.floor(Math.random() * words[category].length)]
console.log(word);
return { word, category };
}
Here is the browser log
When looking at the log we find our state empty

When you use setLettersList(...), you are not actually changing lettersList variable itself. Notice how when you declare it, const [lettersList, setLettersList] = useState([]); letterList is actually constant, so that variable can't even be changed. Instead, it tells React to rerun the function component, this time giving lettersList the new value. Therefore, for the updated value, it will only come once const [lettersList, setLettersList] = useState([]); is rerun again from the entire function being rerun again.
If you want to monitor changes to lettersList, you can do:
useEffect(() => {
console.log(lettersList);
}, [lettersList]);
This will print lettersList every time it is updated.

States updates are asynchronous, so when you set a new state value and console.log() right after it, it's going to show the previous value, because the state hasn't been updated yet.
That's why your lettersList value show the old value of the state in the console (empty array).

Related

Variable in array undefined when trying to use it in an array, but is defined when console logging

I am trying to add dropdown items and handle the click based on the string value of the dropdown item. When I do console.log(props.storedSections[x]), it logs the correct value for the string. However when I try to use it to store the string in to check in handleSectionClick, it is just left as undefined and I have tried wrapping it in {}, {}, and ${}, but none of it works as I intend it to. Any ideas? Also it is a useState variable that is passed down from a parent component but that shouldn't matter since it displays properly when console logged.
here is the declaration of the sections useState object
const [sections, setSections] = useState([<Dropdown.Item onClick={()=>handleSectionClick("Work")}>Work</Dropdown.Item>])
Here is the useEffect function I am using to take the sections variable and add extra items to it using the strings stored in the storedSections useState variable
useEffect(() =>{
//gets the new list without the Add New object
let newSectionList = [...sections.slice(0, sections.length-1)]
for(var x = 0; x < props.storedSections.length; x++){
if(sections.indexOf(props.storedSections[x]) == -1)
//adds the new object that they want to add
newSectionList.push(<Dropdown.Item onClick={()=>handleSectionClick(props.storedSections[x])}>{props.storedSections[x]}</Dropdown.Item>)
}
//then adds in the Add new object again
newSectionList.push(<Dropdown.Item onClick={()=>handleSectionClick("Add New")}>Add New</Dropdown.Item>)
setSections[newSectionList]
setNewSectionTest(newSectionList)
}, [])
Here is the code for handleSectionClick. If I console log the values here they are undefined.
function handleSectionClick(item) {
//if they select the Add New section from the dropdown then bring up the option to type a new one
if (item === "Add New") {
setNewSection(true)
}
//updates the header for the dropdown and keeps track of which section should be saved for the task
else{
setCurrentSection(item)
}
}
In my App.jsx I declare the storedSections like this
const [storedSections, setStoredSections] = useState(['test1', 'test2'])
The main goal of what I am trying to do is to take a dropdown menu with preset items in it, give the user the ability to add their own custom options to the dropdown menu and have the new values persist on a rerender. I plan to move this to a more complicated format including a database that stores each users custom dropdowns, but for now I am just confused why the value is stored properly and is console logged properly but when I try to use it in the declaration of the Dropdown Item, it reads as undefined.
one very obvious problem is var x = 0 in for loop, try changing it to let x = 0; as in below code, and also add dependent props in useEffect dependency array
e.g. useEffect(() => {}, []) here in [] add props.storedSections and sections if it is also coming from props, also handleSectionClick if is hooks returned function
useEffect(() =>{
//gets the new list without the Add New object
let newSectionList = [...sections.slice(0, sections.length-1)]
for(let x = 0; x < props.storedSections.length; x++){
if(sections.indexOf(props.storedSections[x]) == -1)
//adds the new object that they want to add
newSectionList.push(<Dropdown.Item onClick={()=>handleSectionClick(props.storedSections[x])}>{props.storedSections[x]}</Dropdown.Item>)
}
//then adds in the Add new object again
newSectionList.push(<Dropdown.Item onClick={()=>handleSectionClick("Add New")}>Add New</Dropdown.Item>)
setSections[newSectionList]
setNewSectionTest(newSectionList)
}, [props.storedSections, sections])

React - console logs empty an array and then logs a populated array after a second button click

I am running into a slight problem with using React and its hooks. I am trying to print out an array from an API, but it first prints as an empty array to the console, and then only when I click the button again does it prints the array.
Here is the function I'm using to make the array from API data:
const getChampion = () => {
for(let i = 0; i < getData.length; i++){
let individualChamp = champPeep.current.value;
if(getData[i].Name === individualChamp){
// console.log(getData[i]);
setShowChampion(individualChamp);
setChampionTitle(getData[i].Title);
let hitPoints = getData[i].Hp
let attack = getData[i].Attack;
let defense = getData[i].Defense;
let attackRange = getData[i].AttackRange;
let armor = getData[i].Armor;
setRadarData([hitPoints, attack, defense, attackRange, armor]);
console.log(radarData) //returns empty array don't know why
}
} //! Have to click search twice to update array to new array
} //Get Champion name and check to see if it is found in the API
Here is the button the input field that I assigned to this function:
return(
<div>
<div className='search'>
<input ref={champPeep} type="search" id='champion-search' placeholder='e.g Annie' />
</div>
<button onClick={getChampion} className='btn-prim'>Search</button>
</div>
)
And this is what is being logged to the console when I click on button btn-prim:
[]
And when I click the btn-prim button again this is then logged (which is correct):
(5) [524, 2, 3, 625, 19]
Is there something I'm doing wrong?
setState is asynchronous in react, so when you try to log radarData immediately after setRadarData it displays previous data stored in radarData. You can use useEffect hook to log current radarData state
useEffect(() => {
console.log(radarData)
}, [radarData])
why React setStates are async : Why is setState in reactjs Async instead of Sync?
I suggest that instead of you using
console.log(radarData) //returns empty array don't know why
try to add the useEffect hook to log the value of radarData whenever it changed.
Use something like:
useEffect(() => {console.log(radarData)}, [radarData])
State updates will reflect in their next rerender and not immediately. This has already been solved.
Basically your
setRadarData([hitPoints, attack, defense, attackRange, armor]);
console.log(radarData) //returns empty array because its still using the default state {}.
Refer to The useState set method is not reflecting a change immediately.

Can I define a variable within setState in React js?

I am still new to React js.
I am trying to use useState({}) to define an object of objects of orders.
For the newOrderHandler, I am passing the order to be added.
The idea is to add a new object if the order title does not exist and update the amount if the order title already exists in the orders state.
This is the code:
const [orders, setOrders] = useState({});
const newOrderHandler = (newOrder) => {
setOrders(prevOrders => {
console.log('prevOrderss', prevOrders)
// console.log(`prevOrders[newOrder.title]`, prevOrders[newOrder.title])
let newOrders = prevOrders;
if (newOrders[newOrder.title] == null) {
newOrders[newOrder.title] = newOrder
} else {
newOrders[newOrder.title].amount = +prevOrders[newOrder.title].amount + +newOrder.amount
}
return newOrders;
});
};
The problem here is that although when I log the prevOrders to the console, I get it as I wanted:
However, when I calculate the number of objects in the Navigation component, it just displays 0 always.
This is the code that calculates the number of objects in the Navigation component:
Your Cart <span>{Object.keys(props.orders).length}</span>
This is how I passed the props to the Navigation component:
<Navigation orders={orders} />
This always displays 0. I guess the problem is when defining this: let newOrders in the setOrders function, but I am not sure how to solve it.
Thanks in advance.
The problem is that you React cannot detect that you have changed the object. You need to make a copy, you are passing in the same reference.
newOrders == prevOrders returns true.
What is standard is to make a copy so that you do not mutate the state and react can detect that the object has actually changed.
You can use the spread operator.
let newOrders = { ...prevOrders, [newOrder.title] : { ...prevOrders[newOrder.title] }};
if (newOrders[newOrder.title] == null) {
newOrders[newOrder.title] = newOrder
} else {
newOrders[newOrder.title].amount = +prevOrders[newOrder.title].amount + +newOrder.amount
}
return newOrders;
Spreading the nested property too because you are mutating its amount property. For every level of nesting you will have to use spread for the property you want to change.

Select and deselect values with react and hooks

I am trying to change the state by selecting and deselecting the language option in the code below. So far I can update the state by adding a language, my problem is that, if I click on the same language again, I will add it another time to the array. Can anyone explain me how to add or remove the language from the array when clicked one more time?
export default function Dashboard(props) {
const [language, setLanguage] = useState('');
const handleLanguageChange = changeEvent => {
changeEvent.persist()
setLanguage(prevState => [...prevState, changeEvent.target.value])
};
}
It looks like your only issue is your logic in the place where you are handling update. Usage of hooks is correct
So first of all you need to set proper initial value. As you plan to store your languages in an array.
Second part is updating the array. So you can either find clicked language in the array and if it is exist - then use filter to set your new value or filter and compare length of existing array and new one.
const [language, setLanguage] = useState([]);
const handleLanguageChange = changeEvent => {
changeEvent.persist()
setLanguage(prevState => {
const lang = changeEvent.target.value;
if (prevState.includes(lang) {
return prevState.filter(el => el !== lang);
}
return [...prevState, lang]
})
};
You will need a good old check.
if (!languages.includes(changeEvent.target.value) {
// code to add the language
}
Check the selected value using find() method on language array if it returns undefined, then push into array. Rename the state variable as languages otherwise it's confusing (Naming convention standard).
const [languages, setLanguages] = useState('');
const handleLanguageChange = changeEvent => {
changeEvent.persist()
if (!languages.find(value => value == changeEvent.target.value)) {
setLanguages(prevState => [...prevState, changeEvent.target.value])
}
};
2 Things here
Instead of having
<option value="Deutsch">Deutsch</option>
<option value="Englisch">Englisch</option>
use an languages array of json so it bacomes easy for you to add them like
languages= [{value='Deutsch',name= 'Deutsch',...}]
2.setLanguage sa a direct value
setLanguage(changeEvent.target.value)

Add multiple value in array

In my application, in React i have next situation:
I have input where i add different values when i click on save. The value from input is converted from string to array.
So, first time i added a text, i clicked save, and i have 1 value in array.
Second time, i add another text, i click, on save and the first value is changed by second.
I store value in this state:
const [value, setValue] = useState([here comes my value]);
I want to concat the value one after one and i did:
useEffect(()=> {
setAllValues([...value, value])
}, [value])
..but this does't work. How to store all values in one array?
Use the functional form of setState:
setAllValues(prevValue => [...prevValue, newValue])
To perform that operation you would need two states
// one state for array
const [valueArray, setValueArray] = useState([here comes my value]);
// and another state for string
const [value, setValue] = useState('');
// then onSave function
const onSave = () => {
setValueArray([ ...valueArray, value ]);
setValue('');
}

Categories