bad value includes() react - javascript

i'm trying to make a function that add a keyword if is not already in the array, if it is shows a error message, but the problem is that the includes() method doesn't work properly, it shows false first (when a word is not in the array), then true (because it is in the array) and then false again if i don't change the word.
I don't know why this is happening but it happens also with indexOf(). Maybe its a react problem with rendering or something like that.
Between if its another way to take an input value and do this, it is welcome
const [repeatedKeyWord, setRepeatedKeyWord] = useState(false)
let keyWords = []
const addKeyword = () => {
let keyword = document.getElementById('keyword').value;
const exist = keyWords.includes(keyword);
console.log(exist)
if (keyword && !exist){
console.log('in')
keyWords.push(keyword)
setRepeatedKeyWord(false)
}
setRepeatedKeyWord(exist? true : false)
console.log(keyWords)
}
<PlusIcon className="w-6 text-firstColor cursor-pointer mr-2" onClick={addKeyword} />

You must store your keywords in useState, otherwise you lose the value between re-renders of your component. Thereafter, you can use .includes on your array. React ensures that you'll always have the latest value (e.g. 'snapshot' of your state).
Also note that when you are trying to compute a new state value (i.e. updating your array) you are dependent on the previous value of your state. If that is the case, you can pass a function to the setState function. Have a look at this issue where I have included a link to a working codesandbox for updating previous state values.
As a side note, I would suggest to avoid using let to declare your variables. Only use the let keyword if you are certain that you will re-assign a new value to your variable. Otherwise using const might be better to avoid mistakes.
const [keywords, setKeyWords] = useState([])
const addKeyword = () => {
const keyword = document.getElementById('keyword').value;
return setKeywords((prevState) => {
if (prevState.includes(keyword)) {
return [...prevState]
}
return [...prevState, keyword]
})
}
<PlusIcon className="w-6 text-firstColor cursor-pointer mr-2" onClick={addKeyword}

Related

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.

How can I pass innerHTML to an onClick function (Typescript)

I'm trying to pass the Square element's innerHTML to the onClick function. I have also tried to pass in just i but it always is equal to 100. Is there a way to either pass i when it's equal to the same value that goes into the Square or is there a way to pass the innerHTML to the function. Currently, this code generates the error:
[TS: 2532]this is possibly undefined
I'm making a grid of 100 squares, each one is a button, and each one should have it's own ID/number from 1-100 to identify them.
This is what I have currently: Grid of 100 squares arranged in 10x10 formation
export const Square = (props:any) =>{
i += 1;
if(i > 100)
{
i = 1;
}
return(
<DefaultButton styles={factionMapButton} onClick={()=>onSquareClick(this.innerHTML,props.onClick)}>{i}</DefaultButton>
);
}
const onSquareClick = (number:any,isOpen:any) => {
console.log(number);
const panelContent = document.getElementById("panelContent");
if(panelContent !== null)
{
panelContent.innerHTML = number;
}
isOpen();
}
You have quite a few problems.
You should do your best to avoid any in TypeScript, especially not liberally - that defeats the whole purpose of type-checking. If you're trying to fix type problems, you should start by also typing everything properly.
Arrow functions do not have their this altered by the calling context. If there's no enclosing full-fledged function, the this in an arrow function will be the global object or undefined, both of which are useless to you. Either use a function to capture the this, or, even better, use the click event's currentTarget to get a reference to the clicked button.
The .innerHTML of an element returns a string, not an element. If it contains a string that can be coerced to a number, explicitly coerce it to a number instead. (If the HTML content is only the string that can be coerced to the number, you should use .textContent instead - only use .innerHTML when deliberately setting or retrieving HTML markup, not plain text)
A better approach would be to pass down the i to onSquareClick instead of using DOM manipulation - using the closure is much easier
let i = 1;
export const Square = ({ onClick }: { onClick: () => void }) => {
i += 1;
if (i > 100) {
i = 1;
}
return (
<DefaultButton styles={factionMapButton} onClick={(e) => { onSquareClick(i, onClick); }}>{i}</DefaultButton>
);
};
const onSquareClick = (number: number, isOpen: () => void) => {
const panelContent = document.getElementById('panelContent');
if (panelContent !== null) {
panelContent.innerHTML = String(number);
}
isOpen();
};
If you're using React, you should not be using vanilla DOM manipulation like panelContent.innerHTML = number; - instead, set React state that the view uses to determine what should exist in that element. Something like
// Parent component:
const [panelContentText, setPanelContentText] = useState('');
// expand as needed for the other components in your app...
return <div id="panelContent">{panelContentText}</div>
<Square setPanelContentText={setPanelContentText} /* and other props */ />
// ...
// Then call the setPanelContentText prop in onSquareClick
const onSquareClick = (number: number, isOpen: () => void, setPanelContentText: React.Dispatch<React.SetStateAction<string>>) => {
setPanelContentText(String(number));
isOpen();
};
I'd recommend looking into an introductory React tutorial, it looks like you might benefit from learning the process within React's ecosystem, rather than trying to mishmash your understanding of vanilla JS's DOM with React.

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)

How to add dynamic input values to the local state for retrieval

I have a React Native form that allows me to add an Input UI in the form, by clicking a button with this function. This allow me to generate it on the fly. The code for that is this.
addClick() {
this.setState(prevState => ({ values: [...prevState.values, ""] }));
console.log(this.values[0].name);
}
That part works well, but I'm having a problem extracting the data from the dynamic inputs, and add it to an array. So far I have tried this
setVal = value => {
const values = this.state.values[0];
if (values[0].name === "" || values[0].description === "") return;
[...this.state.values, value];
this.setState(values);
console.log(values);
};
How do I organize my states properly so that I can add as many inputs I need, and when I'm finished, I can update the state, and access the new data in my list component?
How do I update my state to the new Array? at the moment, this.state only shows the initial state set at the top.
I'm missing a few things
Please take a look at the full code sandbox HERE so you can see:
See...your created isssue is not so obvious we need to see where you call setVal() function but....
i think you will be more comfortable if you render your <input/> s directly from your state array, not from const x = [] variant. because it seems like you want a dynamic view and in such a cases you will need to bind your loop quantity from state. so:
this.state = {
x: [0,1,2,3,4]
}
and inside your render :
{this.state.x.map(x => {
return (
<TextInput
placeholder={`name${x}`}
value={values[x.toString()]}
handleBlur={() => handleBlur(x.toString())}
onChangeText={handleChange(x.toString())}
style={styles.input}
/>
);
})}

Categories