React and Redux: Manage State Locally First

React and Redux: Manage State Locally First
0

#1

Tell us what’s happening:
I am doing React and Redux: Manage State Locally First challenge
I try the code in local IDE, it works to meet all requirements. But not sure why it doesn’t work here? Thanks

Your code so far


class DisplayMessages extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      input: "",
      messages: []
    };

    this.handleChange = this.handleChange.bind(this);
    this.submitMessage = this.submitMessage.bind(this);
  }
  // add handleChange() and submitMessage() methods here
  handleChange(event){
    var input =event.target.value;
    this.setState({input: input});
  }

  submitMessage(){
    
    this.state.messages.push(this.state.input);
    this.setState({input: ""});
  }
  render() {
    return (
      <div>
        <h2>Type in a new Message:</h2>
        { /* render an input, button, and ul here */ }
        <input type="text" name="usrname" value={this.state.input} onChange={this.handleChange} />
        <button onClick={this.submitMessage}>Add message</button>
        <ul>
          <li></li>
        </ul>
        { /* change code above this line */ }
      </div>
    );
  }
};

Your browser information:

User Agent is: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36.

Link to the challenge:
https://learn.freecodecamp.org/front-end-libraries/react-and-redux/manage-state-locally-first


#2

You also need to loop through an array like it says in the instructions.

Finally, use the ul to map over the array of messages and render it to the screen as a list of li elements.


#3

The first problem is that you are trying to push this.state.input directly into this.state.messages.
Updating the state that way will not work.
Remember this: never update a state without setState().

Next, a better way is to concat the current array messages: this.state.messages with the new information received in input: this.state.input and then assign it into a variable and then use it to update the state through setState()

And finally, in the render function, try to take off that <li></li> because the challenge didn’t ask you to add an <li></li> yet.

You will add an <li></li> only after that you have looped/mapped/forEach… the array messages: this.state.messages in the state. and then print each new value in an <li></li> so that each time you type something in the input and click Add message, a new (typed) element appears on the screen and the final result will be an unordered list of elements that you typed in the input.

I hope this was helpful and
It’s up to you now to do apply all of these.


#4

yes, I forgot to do that…thanks


#5

very helpful. It worked now! Thanks a lot


#6

You’re very welcome.

Good Luck & Happy Coding!


#7

I did something like this but i dont know, iam still having one problem with the unit test:

‘Clicking the Add message button should call the method submitMessage which should add the current input to the messages array in state.’

submitMessage() {
    let mes = this.state.messages;
    
    mes.push(this.state.input);

    this.setState({
      messages: mes,
      input: ''
    });
  }

#8

Instead of push, try concat() in one line and save it into mes variable.

And it will surely work.


#9

You are directly mutating state in the code above. When you write:

let mes = this.state.messages;

You are just assigning the reference of the messages array to mes. So when you push to it, you are actually modifying the same array referenced by this.state.messages.

One way around this, is to make a copy of the messages array like:

let mes = [...this.state.messages];

#10

But why does it work without any warning?

I tried it on FCC and on Codepen.
It works with no problem.

I have no idea why it works while it’s advised not to change the state directly.


#11

Nothing prevents you from mutating state directly. It is just not advised, because of the asynchronous nature of setState. All React documentation states “Do not mutate state”. The tests are wanting to make sure you are not getting into a bad habit which could bite you on a different project. setState batches state updates on it’s own schedule. It is possible if you modified state directly and another part of your app was also using setState, you can have a situation where one of the calls to setState overwrites what you did through a direct mutation. The bottom line is just don’t do it.

Although not discussed in the FCC curriculum, the React documentation should always using a function with setState to guarantee you always have the most current version of state before updating it. There is discussion in the React forums that using functions to update state may become the only way to update state in future versions of React. Knowing this is a possibility and the fact that it always protects you, it would be a good thing to learn now and start implementing in all future React projects.

See the following Codepen I created to see an example of why you should probably always use a function to update state. The main idea of the component is to click each button and add 3 new random numbers to an unordered list. I have created two properties in state to reflect the stored random numbers. I have created two handClick methods, with the first method attempting to setState three times in a row without using a function to do so. The second method attempts the same but uses a function to setState. You will see in the first example, each click of the button only adds one random number to the list and the second example adds 3 random numbers to it’s list. Because setState is asynchronous, the last setState is the only one that sticks in the first example, because the for loop is so fast, that by the time React decides to update state, it only has record of the last call.

const getRandNum = () => Math.floor(100 * Math.random()) + 1;

const List = ({items}) => (
  <ul>
    {items.map((item,i) => <li key={i}>{item}</li>)}
  </ul>
);
                                       
class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      randNums1: [],
      randNums2: []
    };
  }
 
  // regular setState
  handleClick1() {
    for (let i = 0; i < 3; i++) {
      this.setState({
        randNums1: [...this.state.randNums1, getRandNum()] 
      })
    }
  }  

  //using a function to update state
  handleClick2() {
    for (let i = 0; i < 3; i++) {
      this.setState(prevState => ({
        randNums2: [...prevState.randNums2, getRandNum()]  
      }))
    }
  }  
  
  render() {
    return (
      <div className="examples">
        <div>
          <h2>Uses typical setState syntax:</h2>
          <button onClick={this.handleClick1.bind(this)}>Add 3 Numbers</button>
          <div>
            Random Numbers:
            <List items={this.state.randNums1} />
          </div>
        </div>
        <div>
          <h2>Uses function with setState:</h2>
          <button onClick={this.handleClick2.bind(this)}>Add 3 Numbers</button>
          <div>
            Random Numbers:
            <List items={this.state.randNums2} />
          </div>
        </div>
      </div>
    );
  }
}

#12

Very informative.

Thank you.


#13

yeah you are right, never never mutate the state


#14

Thank you @clevious for your comment,


#15

You’re welcome @yamitrvg12.

Good Luck & Happy Coding.