Failed prop type: The prop `actions` is marked as

2019-03-01 02:18发布

I am creating a simple login form using React and Redux. My app.js is:

import React from 'react';
import { render } from 'react-dom';
import Input from 'react-toolbox/lib/input';
import {Button, IconButton} from 'react-toolbox/lib/button';
import PropTypes from 'prop-types';
import * as loginAction from '../actions/loginAction';

class Testing extends React.Component {
    onLoginFormSubmit(event) {
        event.preventDefault();
        this.props.actions.Testing(this.state.username, this.state.password);
    }
    handleChange(name, value){
        let state = this.state;
        state[name] = value;
        this.setState({state});
        console.log(name); // cannot read property of null
        console.log(value); // cannot read property of null
    }

    render() {
        console.log(this.props);
        return (
            <div>
                <form name="Login" onSubmit={(e) => this.onLoginFormSubmit(e)}>
                    <Input type="text" name="username" value="" placeholder="Email Id"  tabIndex="1" onChange={this.handleChange.bind(this, 'username')} />
                    <Input name="password" value="" placeholder="Password" type="password" tabIndex="2" onChange={this.handleChange.bind(this, 'password')} />                  <Button type="submit" className="m-t-20 blue-btn" label="Sign in" tabIndex="3" /> 
                </form>
            </div>
        );
    }
}
Testing.propTypes = {
  loginAction: PropTypes.object.isRequired,

};
function mapStateToProps(state, ownProps) {
  return {
    loginResponse: state.loginResponse
  };
}
function mapDispatchToProps(dispatch) {
  return {
    actions: bindActionCreators(loginAction, dispatch)
  }
}
export default connect(mapStateToProps, mapDispatchToProps)(Testing);

loginAction.js file is:

export function loginError(error){
  return  { error, type: LOGIN_FAILED };
}

export function loginSuccess(response){
  return dispatch => {
    dispatch({ response, type: LOGIN_SUCCESS});
  };
}

export function loginRequest(username, password){
  const user = {username: username, password: password};
  return { user, type: LOGIN_ATTEMPT };
}


export function login(username, password) {
  console.log("User Data: ", username, password);
    return dispatch =>
    fetch('url', {
      method: 'POST',
      headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        username: username,
        password: password
      }),
    })
    .then(response => {
      console.log("I'm here");
      if(response.status >= 200 && response.status < 300){
        console.log("Response; ", response);
        dispatch(loginSuccess(response));
      } else {
        const error = new Error(response.statusText);
        error.response = response;
        dispatch(loginError());
        throw error;
      }
    })
    .catch(error => { console.log('Request Failed: ', error);});
  }

And loginReducer.js file is:

import {
  LOGIN_SUCCESS,
  LOGIN_FAILED,
  LOGIN_ATTEMPT
} from '../actions/loginAction';
import Immutable from 'immutable';

const initialState = new Immutable.Map({
  username: '',
  password: '',
  isLoggingIn: false,
  isLoggedIn: false,
  error: null
});

export default function user(state = initialState, action){
  switch (action.type){
    case LOGIN_ATTEMPT:
      console.log("LOGIN_ATTEMPT: ",action.user);
      return state.merge({
        isLoggingIn: true,
        isLoggedIn: false,
        username: action.user.username,
        password: action.user.password
      });

    case LOGIN_FAILED:
      console.log("LOGIN_FAILED: ");
      return state.merge({
        error: action.error,
        isLoggingIn: false,
        isLoggedIn: false
      });

    case LOGIN_SUCCESS:
      console.log("LOGIN_SUCCESS: ",action);
      return state.merge({
        error: null,
        isLoggingIn: false,
        isLoggedIn: true
      })
      break;

    default:
      return state;

  }
}

When running the page I am getting this error: Failed prop type: The prop actions is marked as required in Testing, but its value is undefined. Also handleChange method is throwing following error: Uncaught TypeError: Cannot set property 'username' of null.

Update: My store.js code is:

import { createStore, applyMiddleware } from 'redux';
import { Provider } from 'react-redux';
import thunk from 'redux-thunk';
import user from '../reducers/loginReducer';

const store = createStore(
  user,
  applyMiddleware(thunk)
);
var routes =(
    <Provider store={store}>
      <Router history={browserHistory}>
        <Route path="/" component={Main}>
        <Route path="/testing" component={Testing}>
      </Router>
    </Provider>
);

I don't want to use redux-form for now.

2条回答
▲ chillily
2楼-- · 2019-03-01 02:25

The function handleChange should get only an event as a parameter.
handleChange(e) this event is attached to the target element, so you can access its values via e.target.value;
With that said, do not bind the hendlers in the render method. do it in the constructor as it will create a new instace of the handler on each render call. bad for performance. As for the redux flow, you should use connect.
export default connect(mapStateToProps, mapDispatchToProps)(Testing).
EDIT
After another look at your code, beside the fact that you didn't use connect to connect the component to redux, you are mapping a wrong object to mapDispatchToProps.
In this code you are using loginAction:

function mapDispatchToProps(dispatch) {
  return {
    actions: bindActionCreators(loginAction, dispatch)
  }
}  

But you never imported it, you used a name import:
import { loginSuccess, loginRequest, login } from '../actions/loginAction';
One possible way to import everything and pass it to mapDispatchToProps is this:
import * as loginAction from '../actions/loginAction';
Another mistake you made is naming this object with different name on propTypes, you named it actions and not loginAction

Testing.propTypes = {
  actions: PropTypes.object.isRequired,

};

You will need the same name:

Testing.propTypes = {
  loginAction: PropTypes.object.isRequired,

};

And again don't forget to connect!!

查看更多
Luminary・发光体
3楼-- · 2019-03-01 02:42

I believe you need to connect your component to redux.

import { connect } from 'react-redux'

 // your code

 export default connect(mapStateToProps, mapDispatchToProps)(Testing)

Don't forget to remove export default from before your class as well.

Edit: Please use setState if you're planning on modifying the local state of a component.

查看更多
登录 后发表回答