Select and deselect values with react and hooks - javascript

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)

Related

How do I update the textarea using useState?

I have a textarea array with values that can be updated. The text values in the array are updated when text is entered into the textarea. The array can also be updated externally.
The problem is that Textarea doesn't want to update its values with setState() like regular text does.
export function GameActions({}) {
const [array, setArray] = useState<Type>([]);
const changeText = (id: number, text: any) => {
actions[id].text = text;
setActions(actions);
};
return {actions.map((action, index) => (<Textarea
defaultValue={action.text}
onChange={(e) =>
changeText(index, e.currentTarget.value)
}
/>))};
};
Provided actions is an array state property, it should be:
setActions((actions)=>{
return actions.map((act,i)=>{
if(i == id) {
act.text = text;
}
return act;
});
});
When a state property is updated using its previous value, the callback argument should be used.
Also, to update an element of a state array, map should be used, rather than the indexation operator [].
Please read this article, to learn how to update state arrays.

bad value includes() react

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}

Display the values from select properly

I have an app where i use a select tag to add values in a table. With this select i can select just one value and to add it in the table.
Some of the code of select input:
const searchPlayer = selectedItems => {
selectedItems = selectedItems.map(name => name.toLowerCase());
let arrayOfMatchedObjects = team.filter(object => {
return selectedItems.some(selectedItem =>
JSON.stringify(object)
.toString()
.toLowerCase()
.includes(selectedItem)
);
});
return arrayOfMatchedObjects;
};
The issue is that it doesn't work properly, because when i select a name but don't save it, and after that i change my mind and i select another name, in the table will be added both names, but i can't understand why, because in this way i add 2 names just clicking once on the button. Who knows how to solve this? link to app: https://codesandbox.io/s/serene-hamilton-go4m3
From your current code, you concate the selection in useEffect(), which causes the multi-selected items to been passed to the table.
Remove the concate method would fit your demand.
Update
Replace this
const addPlayer = () => {
setnewPlayer(searchPlayer(savedPlayer));
};
useEffect(() => {
setSavedPlayer(
Array.from(new Set(savedPlayer.concat(selectedItems)).values())
);
}, [selectedItems]);
to
const addPlayer = () => {
const data = Array.from(new Set(savedPlayer.concat(selectedItems)).values());
setnewPlayer(searchPlayer(data));
setSavedPlayer(data);
};

multiple filters, one onChange function

My structure:
index.js
--> Filters
--> {list}
Filters contains multiple input elements that set the state in index.js via props.
list displays as you guessed it, a list of elements. I now want to filter this list based on the values returned by the filters. Pretty standard I think and found on millions of sites.
The issue is that I want to make the input onChange function reusable. With only one input in the Filters component I had this (shortened):
<input
value={this.state.anySearch}
onChange={this.handleChange}
/>
handleChange = event => {
const value = event.target.value;
this.setState({ anySearch: value });
};
With multiple inputs I tried this, aka reusable for any input:
handleChange = name => event => {
const value = event.target.value;
this.setState({ name: value });
};
onChange={this.handleChange("anySearch")}
But this doesn't work anymore. State now shows only one letter at a time when console logged.
What is best practice to filter according to multiple different criteria à la kayak, and how do I rewrite the handleChange function without pushing each letter into an array?
handleChange = name => event => {
const value = event.target.value;
this.setState({ name: value });
};
your idea of returning a function is correct, you just have an error when setting the state
this.setState({ name: value });
change it to
this.setState({ [name]: value });
Regarding the second question, you can simply iterate over your array and filter out the objects that match that specific criteria, to avoid unnecessary overhead you can implement a caching mechanism that keep track of the searches the user has already done.
something like this:
function filter(criteria) {
if(cache[criteria]) {
return cache[criteria];
}
cache[criteria] = myArray.filter(elem => elem.criteria === criteria);
return cache[criteria];
}

How to remove value from array using index (Ant Design specific)?

I am creating a questionnaire type form using ReactJs and Ant Design. It is a follow up question of How to create a questionnaire type form using Ant Design?
Now I am succeeded in adding new questions and their respective answers but not in removing them. Let's suppose I have added three questions and when I am trying to remove any one of them, its always removing the last one. The related code for removing is as follows:
remove = k => {
console.log(k);
const { form } = this.props;
// can use data-binding to get
const keys = form.getFieldValue("keys");
// We need at least one passenger
if (keys.length === 1) {
return;
}
keys.splice(k, 1);
// can use data-binding to set
form.setFieldsValue({
keys: keys
});
console.log(keys);
};
The complete code can be found as a demo on codesandbox.io.
I have done something similar in the past. Got rid of the boilerplate of antd's remove and replaced with this. Every time I add a row I push that row (object) to formRows array then removing like this:
remove = key => {
const newRows = this.state.formRows.filter(r => r.key !== key)
this.setState(
prev => ({
formRows: newRows
})
)
}

Categories