本文介绍了失败的道具类型:道具`actions`在`Testing`中被标记为必需,但其值为`undefined`的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

从'react'导入React;从'react-dom'导入{渲染};从'react-toolbox/lib/input'导入输入;从'react-toolbox/lib/button' 导入 {Button, IconButton};从 'prop-types' 导入 PropTypes;import * as loginAction from '../actions/loginAction';类测试扩展了 React.Component {onLoginFormSubmit(事件){event.preventDefault();this.props.actions.Testing(this.state.username, this.state.password);}句柄更改(名称,值){让 state = this.state;状态[名称] = 值;this.setState({state});控制台日志(名称);//无法读取 null 的属性控制台日志(值);//无法读取 null 的属性}使成为() {console.log(this.props);返回 (<div><form name="登录" 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"/></表单>

);}}测试.propTypes = {loginAction: PropTypes.object.isRequired,};函数 mapStateToProps(state, ownProps) {返回 {登录响应:state.loginResponse};}函数 mapDispatchToProps(dispatch) {返回 {动作:bindActionCreators(loginAction, dispatch)}}导出默认连接(mapStateToProps,mapDispatchToProps)(测试);

loginAction.js 文件是:

导出函数loginError(error){返回 { 错误,类型:LOGIN_FAILED };}导出函数登录成功(响应){返回调度 =>{调度({响应,类型:LOGIN_SUCCESS});};}导出函数登录请求(用户名,密码){const user = {用户名:用户名,密码:密码};返回 { 用户,输入:LOGIN_ATTEMPT };}导出函数登录(用户名,密码){console.log("用户数据:", 用户名, 密码);返回调度 =>获取('网址',{方法:'POST',标题:{'接受':'应用程序/json','内容类型':'应用程序/json',},正文:JSON.stringify({用户名:用户名,密码:密码}),}).then(响应 => {console.log("我来了");if(response.status >= 200 && response.status < 300){console.log("响应;",响应);调度(登录成功(响应));} 别的 {const error = new Error(response.statusText);error.response = 响应;调度(登录错误());抛出错误;}}).catch(error => { console.log('请求失败:', 错误);});}

而 loginReducer.js 文件是:

import {登录_成功,登录失败,登录_尝试} 来自'../actions/loginAction';从 'immutable' 导入不可变的;const initialState = new Immutable.Map({用户名: '',密码: '',isLoggingIn: 假,isLoggedIn: 假,错误:空});导出默认函数用户(状态 = 初始状态,动作){开关(动作.类型){案例LOGIN_ATTEMPT:console.log("LOGIN_ATTEMPT: ",action.user);返回 state.merge({isLoggingIn: 真,isLoggedIn: 假,用户名:action.user.username,密码:action.user.password});案例登录_失败:console.log("LOGIN_FAILED:");返回 state.merge({错误:action.error,isLoggingIn: 假,isLoggedIn: 假});案例登录_成功:console.log("LOGIN_SUCCESS: ",action);返回 state.merge({错误:空,isLoggingIn: 假,isLoggedIn: 真})休息;默认:返回状态;}}

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

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

import { createStore, applyMiddleware } from 'redux';从'react-redux'导入{提供者};从redux-thunk"导入 thunk;从'../reducers/loginReducer'导入用户;常量存储 = 创建存储(用户,应用中间件(thunk));无功路线=(<提供者商店={商店}><路由器历史={browserHistory}><Route path="/" component={Main}><Route path="/testing" component={Testing}></路由器></提供者>);
解决方案

handleChange 函数应该只获取一个事件作为参数.
handleChange(e) 此事件附加到目标元素,因此您可以通过e.target.value 访问其值;
话虽如此,请不要在 render 函数中bind 处理程序.在 constructor 中执行此操作,因为它会在每次 render 调用时创建 handler 的新实例.对性能不利.至于redux流程,你应该使用connect.
导出默认连接(mapStateToProps, mapDispatchToProps)(Testing).
编辑
再次查看您的代码后,除了您没有使用 connect 将组件连接到 redux 这一事实之外,您还将错误的对象映射到 mapDispatchToProps.
在此代码中,您使用 loginAction:

function mapDispatchToProps(dispatch) {返回 {动作:bindActionCreators(loginAction, dispatch)}}

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

Testing.propTypes = {动作:PropTypes.object.isRequired,};

您将需要相同的名称:

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

再次不要忘记connect!!

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>
);
解决方案

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 handlers in the render function. do it in the constructor as it will create a new instance 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!!

这篇关于失败的道具类型:道具`actions`在`Testing`中被标记为必需,但其值为`undefined`的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

06-29 23:53