Extract-local-state-into-redux

Extract-local-state-into-redux
0

#1

Tell us what’s happening:

I’m doing the beta challenges for react/redux. I’m stuck on this last part. I will copy the directions and highlight the sentence I’m having trouble with:

React and Redux: Extract Local State into Redux
You’re almost done! Recall that you wrote all the Redux code so that Redux could control the state management of your React messages app. Now that Redux is connected, you need to extract the state management out of the Presentational component and into Redux. Currently, you have Redux connected, but you are handling the state locally within the Presentational component.

In the Presentational component, first, remove the messages property in the local state. These messages will be managed by Redux. Next, modify the submitMessage() method so that it dispatches submitNewMessage() from this.props, and pass in the current message input from local state as an argument. Because you removed messages from local state, remove the messages property from the call to this.setState() here as well. Finally, modify the render() method so that it maps over the messages received from props rather than state.

Once these changes are made, the app will continue to function the same, except Redux manages the state. This example also illustrates how a component may have local state: your component still tracks user input locally in its own state. You can see how Redux provides a useful state management framework on top of React. You achieved the same result using only React’s local state at first, and this is usually possible with simple apps. However, as your apps become larger and more complex, so does your state management, and this is the problem Redux solves.

What does this mean to do exactly? I believe I have a pretty good idea of what it wants, but knowing what syntax to use I think is the hard part. Any help?

Your code so far


// Redux:
const ADD = 'ADD';

const addMessage = (message) => {
  return {
    type: ADD,
    message: message
  }
};

const messageReducer = (state = [], action) => {
  switch (action.type) {
    case ADD:
      return [
        ...state,
        action.message
      ];
    default:
      return state;
  }
};

const store = Redux.createStore(messageReducer);

// React:
const Provider = ReactRedux.Provider;
const connect = ReactRedux.connect;

// Change code below this line
class Presentational extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      input: ''
    }
    this.handleChange = this.handleChange.bind(this);
    this.submitMessage = this.submitMessage.bind(this);
  }
  handleChange(event) {
    this.setState({
      input: event.target.value
    });
  }
  submitMessage() {
    this.setState({
      input: ''
    });
  }
  render() {
    return (
      <div>
        <h2>Type in a new Message:</h2>
        <input
          value={this.state.input}
          onChange={this.handleChange}/><br/>
        <button onClick={this.submitMessage}>Submit</button>
        <ul>
          {this.props.messages.map( (message, idx) => {
              return (
                 <li key={idx}>{message}</li>
              )
            })
          }
        </ul>
      </div>
    );
  }
};
// Change code above this line

const mapStateToProps = (state) => {
  return {messages: state}
};

const mapDispatchToProps = (dispatch) => {
  return {
    submitNewMessage: (message) => {
      dispatch(addMessage(message))
    }
  }
};

const Container = connect(mapStateToProps, mapDispatchToProps)(Presentational);

class AppWrapper extends React.Component {
  render() {
    return (
      <Provider store={store}>
        <Container/>
      </Provider>
    );
  }
};

Your browser information:

User Agent is: Mozilla/5.0 (X11; CrOS x86_64 10323.62.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.184 Safari/537.36.

Link to the challenge:


#2

This is what I tried which doesn’t work either:

// Redux:
const ADD = 'ADD';

const addMessage = (message) => {
  return {
    type: ADD,
    message: message
  }
};

const messageReducer = (state = [], action) => {
  switch (action.type) {
    case ADD:
      return [
        ...state,
        action.message
      ];
    default:
      return state;
  }
};

const store = Redux.createStore(messageReducer);

// React:
const Provider = ReactRedux.Provider;
const connect = ReactRedux.connect;

// Change code below this line
class Presentational extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      input: ''
    }
    this.handleChange = this.handleChange.bind(this);
    this.submitMessage = this.submitMessage.bind(this);
  }
  handleChange(event) {
    this.setState({
      input: event.target.value
    });
  }
  submitMessage() {
    dispatch(this.props.submitNewMessage(this.state.input));
    this.setState({
      input: ''
    });
  }
  render() {
    return (
      <div>
        <h2>Type in a new Message:</h2>
        <input
          value={this.state.input}
          onChange={this.handleChange}/><br/>
        <button onClick={this.submitMessage}>Submit</button>
        <ul>
          {this.props.messages.map( (message, idx) => {
              return (
                 <li key={idx}>{message}</li>
              )
            })
          }
        </ul>
      </div>
    );
  }
};
// Change code above this line

const mapStateToProps = (state) => {
  return {messages: state}
};

const mapDispatchToProps = (dispatch) => {
  return {
    submitNewMessage: (message) => {
      dispatch(addMessage(message))
    }
  }
};

const Container = connect(mapStateToProps, mapDispatchToProps)(Presentational);

class AppWrapper extends React.Component {
  render() {
    return (
      <Provider store={store}>
        <Container/>
      </Provider>
    );
  }
};

A bit lost to be honest.


#3

New to react and didnt follow freecodecamp course … but when i use redux with react … i learned you wrap your full app in the provider eg

ReactDOM.render(
<Provider store = {store}>
<App />
</Provider>, 
document.getElementById('root'));

this snippet would be taken form the index.js page … App is a import at the top of file which has all the container used imported into it … and as its wrapped … every where has access to redux …

Would suggest writing out your code in codesandbox if possible and embed it or link to it in a post … this would make it far easier to get you a solution
codesandbox is not too difficult to use … just have to adjust there template slightly to the way you do react …


#4

understanding redux starting off is tricky … added a couple of links to show it in action … its just another way of managing state when app gets bigger.
few things i would recommend first watch videos on react-redux eg https://www.youtube.com/watch?v=qrsle5quS7A&list=PL55RiY5tL51rrC3sh8qLiYHqUV3twEYU_
Also install redux extension for chrome if you use chrome … this will show you what is happening in redux … also react extension for chrome … this shows what happening in react.
These two extensions are a must and really help you understand whats going on.

Added a sandbox with redux being used … might be a bit confusing but i tried to make minimal changes to your code and get this working on codesandbox … did change your actions and reducers so you will have to redo them to your needs … but you have a working version to work with
Edit easier version using just your code all in one file with redux added; bottom link


#5

Thanks, I really appreciate the feedback. I will be looking into these.


#6

Hello @cornielleandres, how did you later get this code to work?


#7

The part that can be problematic:

submitMessage() {
    this.props.submitNewMessage(this.state.input)
    this.setState({
      input: ''
    });

#8

I got as far as you did here. Did you pass the challenge? Any directions or tips?


#9

No sorry. I will have another go at it some time…


#10

@cornielleandres @JohnnyBizzel @Lumie31 @shimphillip

So the way react-redux works is that it injects your state and dispatch as props into your component.

Now when you want to access state, you would grab it from the prop you returned from mapStateToProps.

so…

const mapStateToProps = (state) => {
  return {messages: state}
};

this injects the property messages into your component’s props. To access it you would do so like

this.props.messages // messages state

The same concept applies to the dispatch method.

const mapDispatchToProps = (dispatch) => {
  return {
    submitNewMessage: (message) => {
      dispatch(addMessage(message))
    }
  }
};

// then to dispatch an action
this.props.submitNewMessage('some message')

So just think of it as mapStateToProps and mapDispatchToProps as injecting extra props for you, but you get to define what those props are.

If that doesn’t work for you, another way to see it would be as just another component. Which it actually is since it’s a higher order component

class MapStateOrDispatchToProps extends React.Component {
/*...*/

   render (
      <YourWrappedComponent 
         messages={/*messages from state*/} 
         submitNewMessage={/*...*/}
      />
  )
}

Tell me what I didn’t make clear and I’ll give it another go.


#11

Also there is no validation ,If I click add with empty input it will insert blank

  • !!

  • #12

    in the submit function you wrote :- submitMessage() {
    dispatch(this.props.submitNewMessage(this.state.input));
    this.setState({
    input: ‘’
    });
    } correct the line - dispatch(this.props.submitNewMessage(this.state.input)); by replacing it by - this.props.submitNewMessage(this.state.input);


    #13

    // Redux:
    const ADD = ‘ADD’;

    const addMessage = (message) => {
    return {
    type: ADD,
    message: message
    }
    };

    const messageReducer = (state = [], action) => {
    switch (action.type) {
    case ADD:
    return [
    …state,
    action.message
    ];
    default:
    return state;
    }
    };

    const store = Redux.createStore(messageReducer);

    // React:
    const Provider = ReactRedux.Provider;
    const connect = ReactRedux.connect;

    // Change code below this line
    class Presentational extends React.Component {
    constructor(props) {
    super(props);
    this.state = {
    input: ‘’
    }
    this.handleChange = this.handleChange.bind(this);
    this.submitMessage = this.submitMessage.bind(this);
    }
    handleChange(event) {
    this.setState({
    input: event.target.value
    });
    }
    submitMessage() {
    this.props.submitNewMessage(this.state.input);
    this.setState({
    input: ‘’
    });

    }
    render() {
    return (

        <input
          value={this.state.input}
          onChange={this.handleChange}/><br/>
        <  button onClick={this.submitMessage} >Submit</button>
        <ul>
          {this.props.messages.map( (message, idx) => {
              return (
                 <li key={idx}>{message}</li>
              )
            })
          }
        </ul>
      </div>
    );
    

    }
    };
    // Change code above this line

    const mapStateToProps = (state) => {
    return {messages: state}
    };

    const mapDispatchToProps = (dispatch) => {
    return {
    submitNewMessage: (message) => {
    dispatch(addMessage(message))
    }
    }
    };

    const Container = connect(mapStateToProps, mapDispatchToProps)(Presentational);

    class AppWrapper extends React.Component {
    render() {
    return (



    );
    }
    };


    #14

    This code works for me

    // Redux:
    const ADD = 'ADD';
    
    const addMessage = (message) => {
      return {
        type: ADD,
        message: message
      }
    };
    
    const messageReducer = (state = [], action) => {
      switch (action.type) {
        case ADD:
          return [
            ...state,
            action.message
          ];
        default:
          return state;
      }
    };
    
    const store = Redux.createStore(messageReducer);
    
    // React:
    const Provider = ReactRedux.Provider;
    const connect = ReactRedux.connect;
    
    // Change code below this line
    class Presentational extends React.Component {
      constructor(props) {
        super(props);
        this.state = {
          input: '',
        }
        this.handleChange = this.handleChange.bind(this);
        this.submitMessage = this.submitMessage.bind(this);
      }
      handleChange(event) {
        this.setState({
          input: event.target.value
        });
      }
      submitMessage() {
          this.props.submitNewMessage(this.state.input);
        this.setState({
          input: ''
        });
      
      }
      render() {
        return (
          <div>
            <h2>Type in a new Message:</h2>
            <input
              value={this.state.input}
              onChange={this.handleChange}/><br/>
            <button onClick={this.submitMessage}>Submit</button>
            <ul>
              {this.props.messages.map( (message, idx) => {
                  return (
                     <li key={idx}>{message}</li>
                  )
                })
              }
            </ul>
          </div>
        );
      }
    };
    // Change code above this line
    
    const mapStateToProps = (state) => {
      return {messages: state}
    };
    
    const mapDispatchToProps = (dispatch) => {
      return {
        submitNewMessage: (message) => {
          dispatch(addMessage(message))
        }
      }
    };
    
    const Container = connect(mapStateToProps, mapDispatchToProps)(Presentational);
    
    class AppWrapper extends React.Component {
      render() {
        return (
          <Provider store={store}>
            <Container/>
          </Provider>
        );
      }
    };
    

    #15

    this works:

    submitMessage() {
    this.setState({
    input: ‘’,
    // messages: this.state.messages.concat(this.state.input)
    });
    this.props.submitNewMessage(this.state.input)
    }


    #16

    I had this, except that I was writing this.props.input

    I facepalmed myself

    Thanks for the solution!