Hareesh
Hareesh

Reputation: 1587

How to use reduce function in a nested state object in reactJS?

My state value is

this.state = {
    content: {
        text: {
            tag1: {
                line: "data1"
            }
            tag2: {
                line: "data2"
            }
        }
    }
}

How can I use javascript reduce() function to change the value of line of both tag1 and tag2 to "changed text"?

Upvotes: 3

Views: 1274

Answers (4)

Andrii Golubenko
Andrii Golubenko

Reputation: 5179

Here you are:

this.setState(prevState => {
    return {
        content: {
            ...prevState.content,
            text: Object.keys(prevState.content.text).reduce((newTexts, key) => {
                return {
                    ...newTexts,
                    [key]: {
                        line: "changed text"
                    }
                }
            }, {})
        }
    }
});

Upvotes: 1

Vencovsky
Vencovsky

Reputation: 31595

You should you setState with a function so you don't change state directly.

this.setState(prevState => {        
    for(let k in prevState.content.text){
        prevState.content.text[k].line = "changed";
    } 
    return {content: prevState.content}
}

Edit:

I'm not sure if changing prevState directly is a good thing (please some one correct me), but you can also do

this.setState(prevState => {   
    let changedState = {...prevState}     
    for(let k in changedState.content.text){
        changedState.content.text[k].line = "changed";
    } 
    return {content: changedState.content}
}

Edit:

As said in the comments, {...prevState} is going to be a shallow copy and it can still change the state directly. One solution to this is use lodash cloneDeep

Upvotes: 1

Kunal Mukherjee
Kunal Mukherjee

Reputation: 5853

I don't think Array#prototype#reduce would be a fit.

You can use a plain modern for...of loop with Object.entries and do this:

const state = {
    content: {
        text: {
            tag1: {
                line: "data1"
            },
            tag2: {
                line: "data2"
            }
        }
    }
};

for (const obj of Object.entries(state.content.text)) {
	obj[1].line = 'Changed text';
}

console.log(state);


For acheiving an immutable state you can do Object.assign before mutating the new object's properties.

const state = {
  content: {
    text: {
      tag1: {
        line: "data1"
      },
      tag2: {
        line: "data2"
      }
    }
  }
};

// Create a new object from an existing object
const newState = Object.assign(state, {});

Object.entries(newState.content.text).forEach(x => {
  x[1].line = 'Changed text';
});

console.log(newState);

Upvotes: 0

Maheer Ali
Maheer Ali

Reputation: 36564

I think using for..in will be better.

const state = {
    content: {
        text: {
            tag1: {
                line: "data1"
            },
            tag2: {
                line: "data2"
            }
        }
    }
}

for(let k in state.content.text){
  state.content.text[k].line = "changed";
} 
console.log(state)

Upvotes: 0

Related Questions