失败的道具类型:道具`actions`在`Testing`中标记为必需,但其值为'undefined`

时间:2017-05-12 14:04:17

标签: javascript reactjs redux react-redux

我正在使用React和Redux创建一个简单的登录表单。我的app.js是:

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文件是:

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);});
  }

loginReducer.js文件是:

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;

  }
}

运行页面时出现此错误:道具类型失败:道具actionsTesting标记为必需,但其值为undefinedhandleChange方法也会抛出以下错误:Uncaught TypeError: Cannot set property 'username' of null

更新:我的store.js代码为:

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>
);

我现在不想使用redux-form。

2 个答案:

答案 0 :(得分:1)

函数handleChange应该只将一个事件作为参数 handleChange(e)此事件附加到目标元素,因此您可以通过e.target.value访问其值; 话虽如此,请bind方法中不要render hendlers。在constructor中执行此操作,因为它会在每个handler调用中创建render的新实例。表现不好。 对于redux流程,您应该使用connect export default connect(mapStateToProps, mapDispatchToProps)(Testing)
修改
再看一下您的代码,除了您没有使用connect将组件连接到redux之外,您将错误的对象映射到mapDispatchToProps
在此代码中,您使用的是loginAction

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

但你从未导入它,你使用了名称导入:
import { loginSuccess, loginRequest, login } from '../actions/loginAction';
导入所有内容并将其传递给mapDispatchToProps的一种可能方法是:
import * as loginAction from '../actions/loginAction';
您犯的另一个错误是在propTypes上使用不同的名称命名此对象,您将其命名为actions而不是loginAction

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

};

您需要相同的名称:

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

};

再次不要忘记connect !!

答案 1 :(得分:0)

我认为您需要将组件连接到redux。

import { connect } from 'react-redux'

 // your code

 export default connect(mapStateToProps, mapDispatchToProps)(Testing)

不要忘记在课前删除export default

编辑:如果您计划修改组件的本地状态,请使用setState