3

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"?

2
  • 1
    Why would you use .reduce() to do that? Commented May 10, 2019 at 12:47
  • 1
    .reduce is not suitable for that purpose for a whole lot of reasons, where the first one is that it's goal is to reduce an initial item and meant to work on arrays, while you need to alter an object instead. Just use a regular for..in or .forEach instead on the object keys. Commented May 10, 2019 at 12:49

4 Answers 4

1

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"
                    }
                }
            }, {})
        }
    }
});
Sign up to request clarification or add additional context in comments.

Comments

1

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

1 Comment

{...prevState} won't solve the problem, because it's a shallow copy.
0

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)

2 Comments

This isn't a good answer because in react, you shouldn't change state directly, you should use setState
In the context of react state this answer makes no sense because you are mutating the state.
0

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);

2 Comments

This isn't a good answer because in react, you shouldn't change state directly, you should use setState
You aren't using react's setState. Your answer is correct in the logic, but wrong because it isn't in the context of react

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.