SetState not working with spread operator - reactjs

I have a language switcher in a React native app, it has the selected language ticked, which is stored in state. However this is not working, any advice appreciated. The code is as below. The selectedLanguage is being populated so it knows what needs updating but it's not updating the state object.
constructor(props) {
super(props);
this.state = {
languages: [
{
language: 'Dutch',
selected: false,
},
{
language: 'English',
selected: true,
},
{
language: 'French',
selected: false,
},
{
language: 'German',
selected: false,
},
{
language: 'Polish',
selected: false,
}
],
};
};
changeLanguage(selectedLanguage){
this.state.languages.map((language) => {
if(language.language === selectedLanguage){
this.setState(prevState => ([
...prevState.languages, {
language: selectedLanguage,
selected: true,
}
]));
}
});
}

The spread operator isn’t going to deep compare objects in an array. You’ve got to either move from a languages array to a languages object, as in:
languages: {
Dutch: false,
English: true,
...
}
Or, you need to copy and replace:
changeLanguage(selectedLanguage){
this.state.languages.map((language, index) => {
if(language.language === selectedLanguage){
this.setState(prevState => {
const newLangs = [...prevState.languages];
newLangs[index].selected = true;
return newLangs;
});
}
});
}

First need to find out the right object for given selectedLanguage from an array.
Use Array#map to traverse and update the matched object selected prop value with boolean true and rest to false.
const {languages} = this.state;
const updatedLang = languages.map((lang, key) => {
if (lang.language == selectedLanguage) {
lang.selected = true;
} else {
lang.selected = false;
}
return lang;
})
Now do setState.
this.setState(prevState => ({languages: updatedLang}));

Related

Update nested React state?

I'm trying to update part of a state object that is nested. This is the object:
const [buttonObject, setButtonObject] = useState({
intro: [
{
id: '123',
name: 'first_intro_name',
selected: false,
},
{
id: '124',
name: 'second_intro_name',
selected: false,
},
],
experience: [
{
id: '789',
name: 'first_experience_name',
selected: false,
},
{
id: '8910',
name: 'second_experience_name',
selected: false,
},
],
});
When a button is clicked I want to toggle the selected state. I'm using a click handler that looks like this:
const handleButtonClick = ({ id, selected }) => {
if (id === '123') {
buttonsObject.intro.map(
pref => (pref.selected = pref.id === id ? !pref.selected : selected)
);
setButtonsObject(buttonsObject);
} else if (id === '124') {
buttonsObject.intro.map(
pref => (pref.selected = pref.id === id ? !pref.selected : selected)
);
setButtonsObject(buttonsObject);
}
};
It would handle experiences as well. The issue is that right now it seems like rather than updating the object it just overwrites the object or creates a new one. It also doesnt pass that information back down to the component even though I have it routed correctly so it should.
Is there better/correct syntax for updating nested state like this?
Thanks.
instead of checking again with if condition use higher order array function and spread operator to get optimal solution.
setButtonObject(previous => {
return {
...previous,
info: previous.info.map(item => item.id === id ? {
...item,
selected: true
} ? item)
}
})

How to map trough array of object and toggle boolean property selected

I have state in React functional component. It is and array of objects. Every object in that collection has property "selected", which is a boolean. That array looks like this:
const [filterOptions, setFilterOptions] = useState([
{
title: 'highest',
selected: true,
},
{
title: 'lowest',
selected: false,
},
]);
After handleFilter func is executed I need to set state so this array has same title properties but reverse (toggle) selected properties.
This is handleFilter func in which I need to toggle every selected property of array objects:
const handleFilter = () => {
setFilterOptions();
};
function App() {
const [filterOptions, setFilterOptions] = useState([
{
title: 'highest',
selected: true,
},
{
title: 'lowest',
selected: false,
},
]);
const handleFilter = (e) => {
let newArr = [...filterOptions];
let value = e.target.value;
if (value === "lowest") {
newArr[0].selected = true;
newArr[1].selected = false;
} else if (value === "highest") {
newArr[0].selected = false;
newArr[1].selected = true;
}
setFilterOptions(newArr)
};
return (
<div>
<select onChange={handleFilter}>
<option value="lowest">a</option>
<option value="highest">b</option>
</select>
{console.log((filterOptions))}
</div>
);
}
please check hope it will work
var arryObj =[
{
title: 'highest',
selected: true,
},
{
title: 'lowest',
selected: false,
},
]
const handleFilter = (index,value) => {
arryObj[index].selected = value
};
handleFilter(0,false)
console.log(arryObj)
handleFilter(1,true)
console.log(arryObj)
You can pass a function into setFilterOptions to change the state based on the previous state.
const handleFilter = () => {
setFilterOptions(prevState =>
prevState.map(obj => ({...obj, selected: !obj.selected}))
);
};

Immutable object is not being set in state

The immutable object being returned is correct but this.setState({...}) does not seem to set it.
I've tried the various ways to set state and all seem to have failed.
state = {
onboarding3: fromJS({
selection: {
neverBeen: false,
noConvert: false,
both: false
},
noConvertOptions: {
dynamicCreative: false,
noPurchase: false,
abandoned: false
},
bothOptions: {
dynamicCreative: false,
noPurchase1: false,
noPurchase2: false
}
})
};
updateToggle = (field, option) => {
return () => {
const currentValue = this.state.onboarding3.getIn([field, option]);
const onboarding3 = this.state.onboarding3.setIn([field, option], !currentValue);
this.setState({ onboarding3 });
};
};
<Component
roundedSwitchFunc={this.updateToggle("noConvertOptions", "dynamicCreative")}
defaultChecked={onboarding3.getIn(["noConvertOptions", "dynamicCreative"])}
/>
It was an issue of event bubbling and the data not being in sync. The purpose of this component was for my state to mimic the store but since each component has their own 'store', it does not accurately mimic Redux.

How do I concat two objects into one - React-Native state

Here are two objects one is creating onload of page while another is I want to add on button click into the current state
constructor(props){
super(props);
this.state = {
marked: null,
tempMarkedDates: [],
}
this.toggle = this.toggle.bind(this);
}
// First object into marked is like
console.log(JSON.stringify(this.state.marked));
Output ::
[
{
"2019-01-02":{
"disabled":true,"selected":true,"selectedColor":"#10cc74"
},
"2019-01-04":{
"disabled":true,"selected":true,"selectedColor":"#10cc74"
},
"2019-01-08":{
"disabled":true,"selected":true,"selectedColor":"#10cc74"
},
"2018-12-29":{
"disabled":true,"selected":true,"selectedColor":"#10cc74"
},
"2018-11-27":{
"disabled":true,"selected":true,"selectedColor":"#fc3232"
},
}
]
Working from my own answer here on stack overflow
//Now I am creating new object like
day = "2019-01-10"
let obj = {};
obj[day] = {
disabled: true,
selected: true,
selectedColor: '#fc3232'
}
//So the output will be
[
{
"2018-12-30":{
"disabled":true,"selected":true,"selectedColor":"#10cc74"
}
}
]
I want to merge both the object and update this.state.marked
And it should be remain object after concatenation, while my code is changing it into array
Desire Output - Need to join last or first date object of 30-12-2018
[
{
"2019-01-02":{
"disabled":true,"selected":true,"selectedColor":"#10cc74"
},
"2019-01-04":{
"disabled":true,"selected":true,"selectedColor":"#10cc74"
},
"2019-01-08":{
"disabled":true,"selected":true,"selectedColor":"#10cc74"
},
"2018-12-29":{
"disabled":true,"selected":true,"selectedColor":"#10cc74"
},
"2018-11-27":{
"disabled":true,"selected":true,"selectedColor":"#fc3232"
},
"2018-12-30":{
"disabled":true,"selected":true,"selectedColor":"#10cc74"
}
}
]
So need your help to concat the both object for React native state.
We can use the assign() method of javascript.
Reference: https://stackoverflow.com/a/51143342/3449578
let newstate = Object.assign(obj , this.state.marked[0])
this.setState({marked: newstate });
You can use ... spread operator inside this.setState:
var obj = {
"2019-01-02":{
"disabled":true,"selected":true,"selectedColor":"#10cc74"
},
"2019-01-04":{
"disabled":true,"selected":true,"selectedColor":"#10cc74"
},
"2019-01-08":{
"disabled":true,"selected":true,"selectedColor":"#10cc74"
},
"2018-12-29":{
"disabled":true,"selected":true,"selectedColor":"#10cc74"
},
"2018-11-27":{
"disabled":true,"selected":true,"selectedColor":"#fc3232"
},
}
console.log(obj)
var s = {...obj,
"2018-12-30":{
"disabled":true,"selected":true,"selectedColor":"#10cc74"
}
}
console.log(s)
In React you can do this by:
this.setState({ marked: {...this.state.marked, "2018-12-30":{
"disabled":true,"selected":true,"selectedColor":"#10cc74"
}})
This is how I would do it in react. Keep your whole state and update the marked with new values using setState.
Just provide values to the handleMarked function to update the state. And delete dummy values for day and value.
constructor(props) {
super(props);
this.state = {
marked: {},
tempMarkedDates: [],
}
this.toggle = this.toggle.bind(this);
}
handleMarked = (day, value) => {
/* Provide values to function params above and delete dummy values below */
day = '2019-01-10';
value = {
disabled: true,
selected: true,
selectedColor: '#fc3232'
}
this.setState({
...this.state,
marked: {
...this.state.marked,
[day]: value
}
},
() => console.log(JSON.stringify(this.state.marked))
);
}

React state variable changes unpredictably

I'm trying to write a front-end using React for the first time.
I stumbled upon a problem trying to give users the option to order a list of React components and later undo the ordering.
So what I tried to do is save the initial list order in a separate state variable skills_initial. This is done by cloning the skills list. If the ordering gets undone, the skills_initial state variable is used to reset it to the initial order.
class Day extends React.Component {
constructor(props) {
super(props);
var skills = [];
var skill;
var xp;
for (var i in skill_names) {
skill = skill_names[i];
xp = this.props.end[skill] - this.props.start[skill];
skills.push(<Skill name={skill} xp={xp} key={skill}/>);
}
var skills_clone = [];
for (var k=0; k < skills.length; k++) {
skills_clone.push(skills[k]);
}
this.state = {skills: skills, skills_initial: skills_clone, descend: false, ascend: false};
this.descend = this.descend.bind(this);
this.ascend = this.ascend.bind(this);
}
descend() {
document.getElementById('ascend').checked = false;
if (this.state.descend) {
document.getElementById('descend').checked = false;
this.setState(prevState => ({
skills: prevState.skills_initial,
descend: false,
ascend: false
}));
} else {
this.setState(prevState => ({
skills: prevState.skills.sort(
function(skill1, skill2) {
return skill2.props.xp - skill1.props.xp;
}),
descend: true,
ascend: false
}));
}
}
ascend() {
document.getElementById('descend').checked = false;
if (this.state.ascend) {
document.getElementById('ascend').checked = false;
this.setState(prevState => ({
skills: prevState.skills_initial,
ascend: false,
descend: false,
}));
} else {
this.setState(prevState => ({
skills: prevState.skills.sort(
function(skill1, skill2) {
return skill1.props.xp - skill2.props.xp;
}),
ascend: true,
descend: false
}));
}
}
render() {
return (
<ol id="skill_list_vertical" style={{listStyleType: "none"}}>
<input id="descend" type="radio" onClick={this.descend}/>descend
<input id="ascend" type="radio" onClick={this.ascend}/>ascend
{this.state.skills}
</ol>
);
}
}
var skill_names = [
"attack",
"defence",
"strength",
"constitution",
"ranged",
"prayer",
"magic",
"cooking",
"woodcutting",
"fletching",
"fishing",
"firemaking",
"crafting",
"smithing",
"mining",
"herblore",
"agility",
"thieving",
"slayer",
"farming",
"runecrafting",
"hunting",
"construction",
"clue_easy",
"clue_medium",
"bounty_rogue",
"bounty_hunter",
"clue_hard",
"LMS",
"clue_elite",
"clue_master"
];
So the first two times the radio button is checked, the list is correctly ordered. Only upon trying to undo the ordering the second time, the list remains ordered. In no place the state variable skills_initial is changed unless in the constructor which I thought to be only called once.
Any further advice on my code structure / react habits is greatly appreciated.

Resources