本文介绍了失败的道具类型:道具`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: 真})休息;默认:返回状态;}}
运行页面时出现此错误:道具类型失败:道具 actions
在 Testing
中标记为必需,但其值为 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`的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!