Seth Spivey
Seth Spivey

Reputation: 367

How to properly dispatch variable through Redux action / reducer

I'm trying to pass my isMatched variable through my action and conditionally change the boolean value based on the json response sent from the post route.

However, my current setup always returns a payload of undefined. What am I missing here within this to successfully change my values based on the data?

    onSubmit = e => {
      e.preventDefault();


      const resetPassword = {
        email: this.username,
        password: this.state.password,
        password2: this.state.password2,
        isMatched: false
      };

      this.props.updateUserPassword(resetPassword);

    };



    // Reset Password action to dispatch isMatched to reducer

    export const updateUserPassword = resetPassword => dispatch => {
     axios
      .post("/api/users/resetpassword", resetPassword)
      .then(res => {

      if (res.data.msg === 'passwords match') {

        const isMatched = true;

        dispatch(setMatchedPass(isMatched));

      } else {

        const isMatched = false;

      }
    })
    };

    // Matched Password Dispatch
    export const setMatchedPass = isMatched => {
     return {
       type: SET_MATCH_PASS,
       payload: isMatched
       ***** If I set this to 'testing', that's output, but if *****
       ***** I try to dispatch the variable isMatched, it doesn't *****
       ***** seem to pass the value. *****
     };
    };


    // Matched Password Reducer

    import { SET_MATCH_PASS } from "../actions/types";

    const matchedState = {
      isMatched: false
    };

    export default function (state = matchedState, action) {

    switch (action.type) {

      case SET_MATCH_PASS:
        return {
          ...state,
          isMatched: action.payload
        };
      default:
        return state;

     } 

    }

EDIT: Defining resetPassword to pass values to updateUserPassword

/* Connect Mapstate to props */
export default connect(mapStateToProps, { updateUserPassword })(ResetPassword);
      onSubmit = e => {
        e.preventDefault();


        const resetPassword = {
          email: this.username,
          password: this.state.password,
          password2: this.state.password2,
          isMatched: false
        };

        this.props.updateUserPassword(resetPassword);

      };


    // This takes resetPassword, sets isMatched and dispatches it to setMatchedPass


    export const updateUserPassword = resetPassword => dispatch => {
      axios
        .post("/api/users/resetpassword", resetPassword)
        .then(res => {

          const isMatched = true;
          dispatch(setMatchedPass(isMatched));

        })
        .catch(err =>
          dispatch({
            type: GET_ERRORS,
            payload: err.response.data
          })
        );



    //** To pass isMatched through type SET_MATCH_PASS

    export const setMatchedPass = isMatched => {

      console.log('setting isMatched for SET_MATCH_PASS action:', isMatched);

      return {
        type: SET_MATCH_PASS,
        payload: isMatched
      };

    };


    //** MatchReducer to receive the isMatched property and change the 
    //** state of isMatched in Store

    import { SET_MATCH_PASS } from "../actions/types";

    const matchedState = {
      isMatched: false
    };

    export default function (state = matchedState, action) {

      switch (action.type) {

        case SET_MATCH_PASS:
          return {
            ...state,
            isMatched: action.payload
          };
        default:
          return state;

      }

    }



     //// **** TO RENDER THE COMPONENT **** ////


    const MatchedComp = ({ isMatched }) => {
      return (
        <div>
          {isMatched && <div>If you see this, the password is correct</div>}
        </div>
      );

    };

    MatchedComp.propTypes = {
      isMatched: PropTypes.bool,
    };



    /// This is called in my return ///
    <MatchedComp {...this.props} />



    //** Mapping props / state **//
    const mapStateToProps = state => {

      console.log(state);
      return {
        auth: state.auth,
        errors: state.errors,
        isMatched: state.isMatched
      }
    };


    ////*** OUTPUT OF console.log(state); ***////


    {auth: {…}, match: {…}, errors: {…}}
     auth: {
       isAuthenticated: false, 
       user: {…}, 
       loading: false
     }
     errors: {}
     match:
      isMatched: true
    __proto__: Object
    __proto__: Object


    // *** Output of {console.log('in render', isMatched)} *** ///

    **in render undefined**

Upvotes: 5

Views: 1996

Answers (5)

Seth Spivey
Seth Spivey

Reputation: 367

Here's how I finally cracked the export default connect of multiple components. As the answers before suggest, you can't export more than one connect per component, so I removed everything to do with MatchedComp and the rendering and stored it in another file with the following code:

import PropTypes from "prop-types";
import React from "react";
import { connect } from "react-redux";

const MatchedComp = ({ isMatched }) => {
  return (
    <div>
      {console.log('in render: ', isMatched)}
      {isMatched && <div>If you see this, the password is correct</div>}
    </div>
  );
};

MatchedComp.propTypes = {
  isMatched: PropTypes.bool,
};

const mapStateToProps = state => {
  return {
    isMatched: state.match.isMatched,
  }
};

export default connect(mapStateToProps)(MatchedComp);

I then imported the MatchedComp into my ResetPassword file.

import MatchedComp from "../../components/auth/MatchedPassword";

Upvotes: 0

Uma
Uma

Reputation: 846

It's hard to debug without having the whole picture. But make sure that the type you use is the same in actions and reducer.

To see how data travels and where it breaks.

// Matched Password Dispatch
export const setMatchedPass = isMatched => {
 console.log('setting isMatched for SET_MATCH_PASS action: ', isMatched, SET_MATCH_PASS);
 return {
   type: SET_MATCH_PASS, // make sure this type is the same as in reducer
   payload: isMatched
 };
};

Then check in reducer you - both the action and payload:

export default function (state = matchedState, action) {
     console.log('Checking again', action.type, action.payload);
     switch (action.type) {
          case SET_MATCH_PASS:
               ...
          default:
     return state;

     }
}

EDIT (Update based on your state console log): to show how to retrieve value from redux

import React from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';


const MyComponent = ({
  isMatched
}) => {
  /**
   * It will re-render each time the value of `isMatched` is changed
   */
  return (
    <div>
      {console.log('in render', isMatched) // what value do you see here?}
      {isMatched && <div>If you see this, the password is correct</div>}
    </div>

  );
};

MyComponent.propTypes = {
  isMatched: PropTypes.bool,
};

const mapStateToProps = state => {
  // your value on state is stored in `match` 
  return {
    isMatched: state.match.isMatched,
  }
};

export default connect(
  mapStateToProps,
  null
)(MyComponent);

Upvotes: 3

rishichawda
rishichawda

Reputation: 453

From what I can see at the first look (although the code looks incomplete so maybe you already have it), but there is no bindActionCreator call happening on the component in the connect HOC. In simpler terms, you need to pass either an object with your actions to the second param of the connect call, something like this:


Component.js

import { connect } from 'react-redux';
import { updateUserPassword } from 'actions'; 

const Component = () => {
  // component code
  onSubmit = e => {
    e.preventDefault();


    const resetPassword = {
      email: this.username,
      password: this.state.password,
      password2: this.state.password2,
      isMatched: false
    };

    this.props.updateUserPassword(resetPassword);

  };
}


// **The second argument is what you missed, I think.**

connect(mapStateToProps, {
  updateUserPassword: updateUserPassword,
})(Component)

actions.js

export const updateUserPassword = resetPassword => dispatch => {
 axios
  .post("/api/users/resetpassword", resetPassword)
  .then(res => {

  if (res.data.msg === 'passwords match') {

    const isMatched = true;

    dispatch(setMatchedPass(isMatched));

  } else {

    const isMatched = false;

  }
})
};

It should work then.

Upvotes: 1

Vu Luu
Vu Luu

Reputation: 790

What did you use to handle async updateUserPassword action? For example:redux-thunk,redux-saga

Upvotes: 0

DDS
DDS

Reputation: 4375

In your mapStateToProps you're accessing state.isMatched but you should use state.match.isMatched as shown in your logged state.

Upvotes: 4

Related Questions