Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is this.props undefined when I'm passing in actions to mapDispatchToProps?

tl;dr: In the LoginForm, this.props is undefined despite my passing in actions in mapDispatchToProps.

I've set breakpoints in the connect function, and the action is making it into the connect function, but for whatever reason, in the handleSubmit function, this.props is undefined.

I'm stumped.

I have a separate 'SignUp' flow that's basically identical to what I have below, and it works perfectly.

I essentially copy pasted the sign up code, and renamed everything "login", and modified things slightly and for whatever reason this.props is undefined, even though what I'm passing into mapDispatchToProps seems to be correct.

The component:

import React from 'react';
import { connect } from 'react-redux';

import { loginSubmit, loginUpdateField } from '../../redux/actions/login.actions';
import { TextInput } from '../../stories/TextInput';
import { Button } from '../../stories/Button';

export class LoginForm extends React.Component {
  // eslint-disable-next-line
  constructor(props) {
    super(props);
  }

  handleSubmit(event) {
    event.preventDefault();
    console.log('Props: ', this.props); // UNDEFINED!!!!!!
  }

  handleUpdateField(field, value) {
    // this.props.updateField(field, value);
  }

  render() {

    return (
      <form style={{ 'WebkitPerspective': '1000' }}>
        <TextInput label="Username" id="username" /> <br />
        <TextInput label="Password" id="password" type="password" /> <br /><br />

        <Button disabled={false} primary size="medium" onClick={this.handleSubmit.bind(this)} label="Ready for Takeoff" />
        <Button disabled={false} size="medium" onClick={() => alert} label="Cancel" />
      </form>
    );
  }
}

export default connect(
  null,
  { loginSubmit, loginUpdateField }
)(LoginForm);

The action:

export const loginSubmit = (fieldValues) => {
  return dispatch => {
    dispatch(loginSubmitBegin);

    let errors = isFormValid(fieldValues);

    if (!errors.formValid) {
      dispatch(loginErrors(errors));
    } else {
      axios
        .post(`https://api.mydomain.io/login`, fieldValues, { headers: { "Access-Control-Allow-Origin": "*", } })
        .then(res => {
          dispatch(loginSuccess({ username: 'testuser' }));
        })
        .catch(err => {
          handleErr(err.response.data.error);
        });
    }
  }
};

The reducers:

const initialState = {
  loginStatus: 'not_started',
  fieldValues: {
    username: '',
    password: '',
  },
  errors: {
    formValid: false,
    username: [],
    password: [],
  }
};

export default function (state = initialState, action) {
  switch (action.type) {
    case 'LOGIN_UPDATE_FIELD':
      let newState = {
        ...state,
      };
      newState.fieldValues[action.payload.fieldName] = action.payload.fieldValue;

      return newState;

    case 'LOGIN_SUBMIT_BEGIN':
      return {
        ...state,
        signupStatus: 'pending'
      };

    case 'LOGIN_ERRORS':
      return {
        ...state,
        signupStatus: 'errors',
        errors: action.payload
      };

    case 'LOGIN_SUCCESS':
      return {
        ...state,
        signupStatus: 'success'
      };

    default:
      return state;
  }
}

Combined Reducer:

import { combineReducers } from 'redux';

import signup from './signup.reducers';
import user from './user.reducers';
import login from './login.reducers';

export default combineReducers({ login, user, signup});

LoginPage.js

import React from 'react';
import PropTypes from 'prop-types';

import { LoginForm } from '../features/LoginForm';
import './signup.css';

export const LoginPage = () => {
  return (
    <article>
      <section>
        <h2>Login!!</h2> <br /> <br />
        <LoginForm />
      </section>
    </article>
  );
};

LoginPage.propTypes = {
  user: PropTypes.shape({})
};

LoginPage.defaultProps = {
  user: null,
};
like image 560
Tyler Biscoe Avatar asked Nov 06 '22 05:11

Tyler Biscoe


1 Answers

If you import the named export from this component, then you're importing the unconnected component:

import { LoginForm } from "./LoginForm";

Since the redux-connected component is the default export, you have to make sure to import the default export if you want it to be the redux-connected version.

import LoginForm from "./LoginForm";

One way to avoid this mistake in the future is to not even export the unconnected LoginForm component. In other words, you can just declare the class as class LoginForm extends React.Component { ... } rather than export class LoginForm extends React.Component { ... }.

like image 59
Nick Avatar answered Nov 12 '22 19:11

Nick