javascriptreduxreact-reduxreact-redux-form

checkPropTypes.js:20 Warning: Failed prop type: The prop `form` is marked as required in `LoginPage`, but its value is `undefined`


I am stuck on same bug for 2 days now and I can t find fix. I was hoping somebody would help me out with this one. It seems that error is on form: PropTypes.array.isRequired, in PropTypes but I don t get why. I mean I initialized state to empty array in formReducer. I would appreciate any help cause I just started Learning React and React-Redux is a bit overwhelming for me but I really want to learn it.

loginPage

import React from "react";
import { connect } from "react-redux";
import * as formAction from "../../redux/actions/formAction";
import PropTypes from "prop-types";

class LoginPage extends React.Component {
  constructor() {
    super();

    this.state = {
      visitor: {
        username: "",
        password: "",
      },
    };
    //this.updateVisitor = this.updateVisitor.bind(this);
  }

  updateVisitor(attr, event) {
    console.log(attr + " == " + event.target.value);

    const updatedVisitor = { ...this.state.visitor }; //ili je object assign vrati se na 27.47
    updatedVisitor[attr] = event.target.value;

    this.setState({
      visitor: updatedVisitor,
    });
  }

  register(event) {
    event.preventDefault();
    this.props.dispatch(formAction.insertID(this.state.visitor));
    console.log("REGISTER:" + JSON.stringify(this.state.visitor));
  }

  login(event) {
    event.preventDefault();
    console.log("LOGIN:" + JSON.stringify(this.state.visitor));
  }

  render() {
    return (
      <div className="container">
        <div className="row">
          <div className="col-md-6">
            <h1>Register</h1>
            <form onSubmit={this.register.bind(this)}>
              <input
                onChange={this.updateVisitor.bind(this, "username")}
                className="form-control"
                type="text"
                placeholder="Username"
              ></input>
              <br />

              <input
                onChange={this.updateVisitor.bind(this, "password")}
                className="form-control"
                type="password"
                placeholder="Password"
              ></input>

              <br />
              <button type="submit" value="save">
                Register
              </button>
            </form>

            <hr />

            <h1>Login</h1>
            <form onSubmit={this.login.bind(this)}>
              <input
                onChange={this.updateVisitor.bind(this, "username")}
                className="form-control"
                type="text"
                placeholder="Username"
              ></input>

              <br />
              <input
                onChange={this.updateVisitor.bind(this, "password")}
                className="form-control"
                type="password"
                placeholder="Password"
              ></input>
              <br />

              <button type="submit" value="save">
                Log in
              </button>
              {this.props.form.map((Zasvaki) => (
                <div key={Zasvaki.username}>{Zasvaki.username}</div>
              ))}
            </form>
          </div>
        </div>
      </div>
    );
  }
}

LoginPage.propTypes = {
  dispatch: PropTypes.func.isRequired,
  form: PropTypes.array.isRequired,
};

function mapStateToProps(state) {
  return {
    form: state.form,
  };
}

export default connect(mapStateToProps)(LoginPage);

configureStore

import rootReducer from "./reducers/formReducer";
import reduxImmutableStateInvariant from "redux-immutable-state-invariant";

export default function configureStore(initialState) {
  const composeEnhancers =
    window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose; //Add support for redux devtools
  return createStore(
    rootReducer,
    initialState,
    composeEnhancers(applyMiddleware(reduxImmutableStateInvariant()))
  );
}

formAction

export function insertID(ID) {
  return { type: "INSERT_ID", ID: ID };
}

formReducer

  switch (action.type) {
    case "INSERT_ID":
      return [...state, { ...action.ID }]; //Vraca klonirani array sa svim prijasnjim stanima plus sa novim stanjem dodanim
    default:
      return state;
  }
}

index.js //Combine reducer

import form from "./formReducer";

const rootReducer = combineReducers({
  form: form,
});

export default rootReducer;

Solution

  • So the reducer doesn't have name in your case, so when you assing values to the form just assign the whole state:

    function mapStateToProps(state) {
      console.log("there's no 'form' on the state: ", state.form); // undefined
      // looks like you need to assign whole state
      return { form: state };
    }
    

    and now you wont need to do this.props.form && part in:

    this.props.form && this.props.form.map((Zasvaki) => (
    

    ^^ that would fix your error, BUT what I really suggest you doing is to make redux state an object and actually assign the value you want to a property form in your case. So in reducer you will have something like this:

    export default function formReducer(state = {}, action) {
      switch (action.type) {
        case "INSERT_ID":
          return {
            ...state,
            form: {
              ...action.ID
            }
          }
        default:
          return {
            ...state
          }
      }
    }
    

    Then in loginPage.js you would need following changes:

    Auth.propTypes = {
      dispatch: PropTypes.func.isRequired,
      // most likely you will not need an array of user names and passwords
      // change back to the array if otherwise
      form: PropTypes.shape({
        username: PropTypes.string,
        password: PropTypes.string,
      }),
    };
    
    function mapStateToProps(state) {
      return { form: state.form };
    }
    export default connect(mapStateToProps)(Auth);
    

    and in the html just display properties of the form you want to see:

    {this.props.form && (
      <div key={this.props.form.username}>{this.props.form.password}</div>
    )}