javascript - MERN Stack - TypeError:无法将未定义或 null 转换为对象
问题描述
我正在关注 Traversy Media 的 MERN Stack,从前到后课程。我正在尝试设置应用程序的登录部分。我确保我的代码与他的完全相同。但我得到了错误TypeError: Cannot convert undefined or null to object
,而他没有。
我一直在 github 上阅读,人们说这是 reactstrap 的问题,与依赖关系有关吗?这超出了我的想象。我尝试运行 npm ci,尝试更改 json-package-lock 文件,尝试制作 npm-shrinkwrap 文件等,如此处所述https://github.com/reactstrap/reactstrap/issues/1373和此处https: //github.com/reactstrap/reactstrap/issues/1374但到目前为止没有任何工作,它会产生完全相同的错误。
authActions.js:
import axios from "axios";
import setAuthToken from "../utils/setAuthToken";
import jwt_decode from "jwt-decode";
import { GET_ERRORS, SET_CURRENT_USER } from "./types";
// Register User
export const registerUser = (userData, history) => dispatch => {
axios
.post("/api/users/register", userData)
.then(res => history.push("/login"))
.catch(err =>
dispatch({
type: GET_ERRORS,
payload: err.response.data
})
);
};
// Login - Get User Token
export const loginUser = userData => dispatch => {
axios
.post("/api/users/login", userData)
.then(res => {
// Save to localStorage
const { token } = res.data;
// Set token to ls
localStorage.setItem("jwtToken", token);
// Set token to Auth header
setAuthToken(token);
// Decode token to get user data
const decoded = jwt_decode(token);
// Set current user
dispatch(setCurrentUser(decoded));
})
.catch(err =>
dispatch({
type: GET_ERRORS,
payload: err.response.data
})
);
};
// Set logged in user
export const setCurrentUser = decoded => {
return {
type: SET_CURRENT_USER,
payload: decoded
};
};
仪表板.js:
import React, { Component } from "react";
import { Link } from "react-router-dom";
import PropTypes from "prop-types";
import { connect } from "react-redux";
import { getCurrentProfile } from "../../actions/profileActions";
import Spinner from "../common/Spinner";
class Dashboard extends Component {
componentDidMount() {
this.props.getCurrentProfile();
}
render() {
const { user } = this.props.auth;
const { profile, loading } = this.props.profile;
let dashboardContent;
if (profile === null || loading) {
dashboardContent = <Spinner />;
} else {
// Check if logged in user has profile data
if (Object.keys(profile).length > 0) {
dashboardContent = <h4>TODO: DISPLAY PROFILE</h4>;
} else {
// User is logged in but has no profile
dashboardContent = (
<div>
<p className="lead text-muted">Welcome {user.name}</p>
<p>You have not yet setup a profile, please add some info</p>
<Link to="/create-profile" className="btn btn-lg btn-info">
Create Profile
</Link>
</div>
);
}
}
return (
<div className="dashboard">
<div className="container">
<div className="row">
<div className="col-md-12">
<h1 className="display-4">Dashboard</h1>
{dashboardContent}
</div>
</div>
</div>
</div>
);
}
}
Dashboard.propTypes = {
getCurrentProfile: PropTypes.func.isRequired,
auth: PropTypes.object.isRequired,
profile: PropTypes.object.isRequired
};
const mapStateToProps = state => ({
profile: state.profile,
auth: state.auth
});
export default connect(
mapStateToProps,
{ getCurrentProfile }
)(Dashboard);
预期:加载一个我可以登录的页面,然后当我登录时它应该说“欢迎,user.name
。您尚未设置个人资料,请添加一些信息”
实际结果:
TypeError: Cannot convert undefined or null to object
Dashboard.render
src/components/dashboard/Dashboard.js:23
20 | dashboardContent = <Spinner />;
21 | } else {
22 | // Check if logged in user has profile data
> 23 | if (Object.keys(profile).length > 0) {
| ^ 24 | dashboardContent = <h4>TODO: DISPLAY PROFILE</h4>;
25 | } else {
26 | // User is logged in but has no profile
此外,在离开它一段时间后,如果我回到它,它会加载我可以登录的页面,但是当我使用我在数据库中注册的用户的登录信息时,它会给我这个错误:
(anonymous function)
src/actions/authActions.js:39
36 | .catch(err =>
37 | dispatch({
38 | type: GET_ERRORS,
> 39 | payload: err.response.data
40 | })
41 | );
42 | };
当我重新加载时,它会返回给我上面显示的另一个错误(TypeError: Cannot convert undefined or null to object
错误)
解决方案
您的代码有几个问题。最明显的是您如何检查profile
:
if (profile === null || loading)
我相信正在发生的事情是profile
被设置为undefined
并且loading
被设置为false
,因此,它正在通过if
声明。
相反,profile
最初设置为空对象{}
。然后,您可以使用 lodash 的isEmpty()
函数来检查它是否仍然为空。这也将使您的propTypes
验证保持为 1:1。如果它是一个对象,它仍然是一个对象。如果它是一个字符串,它保持一个字符串,依此类推。再次保持 1:1。
此外,在类型检查道具时,描述shape
. object
有时,您最喜欢 use eslint
,它会抛出有关使用Proptypes.array
和之类的通用描述符的错误Proptypes.object
。虽然这可能看起来有点矫枉过正,但如果属性与描述的形状不同,它可以突出显示对象中的错误。
工作代码框:
这个例子包括一些高级/中级代码的使用,所以我想其中一些没有意义。如果您有任何问题,请随时提问。
在此代码示例中使用:ES6 对象解构、带有简化返回的胖箭头函数、胖箭头类属性、展开运算符和三元运算符。
您的代码的重构和简化版本...
容器/仪表板
import isEmpty from "lodash/isEmpty";
import React, { PureComponent } from "react";
import PropTypes from "prop-types";
import { connect } from "react-redux";
import { getCurrentProfile } from "../../actions/profileActions";
import DisplayUser from "../../components/DisplayUser";
import DisplaySignUp from "../../components/DisplaySignUp";
import Spinner from "../../components/Spinner";
// using a PureComponent because we're not utilizing state,
// but we're utilizing the "componentDidMount" lifecycle
class Dashboard extends PureComponent {
componentDidMount = () => {
this.props.getCurrentProfile(1);
}
// the below can be read like so:
// if "isLoading" is true... then show a spinner
// else if "currentUser" is not empty... then display the user details
// else show a signup message
render = () => (
this.props.isLoading ? (
<Spinner />
) : !isEmpty(this.props.currentUser) ? (
<DisplayUser currentUser={this.props.currentUser} /> /
) : (
<DisplaySignUp />
)
}
// describing the shape of the "currentUser" object
// notice that there aren't any required declarations
// within the object itself because "currentUser" is initially
// an empty object; however, when it's not empty, it should
// follow this structure
Dashboard.propTypes = {
getCurrentProfile: PropTypes.func.isRequired,
currentUser: PropTypes.shape({
id: PropTypes.number,
name: PropTypes.string,
username: PropTypes.string,
email: PropTypes.string,
address: PropTypes.shape({
street: PropTypes.string,
suite: PropTypes.string,
city: PropTypes.string,
zipcode: PropTypes.string,
geo: PropTypes.objectOf(PropTypes.string)
}),
phone: PropTypes.string,
website: PropTypes.string,
company: PropTypes.objectOf(PropTypes.string)
}).isRequired,
isLoading: PropTypes.bool.isRequired
};
export default connect(
state => ({
currentUser: state.profile.currentUser,
isLoading: state.profile.isLoading
}),
{ getCurrentProfile }
)(Dashboard);
减速器/profileReducer
import * as types from "../types";
const initialState = {
currentUser: {}, // setting currentUser initially as an empty object
isLoading: true // setting isLoading initially as a boolean
};
export default (state = initialState, { type, payload }) => {
switch (type) {
case types.SET_SIGNEDIN_USER:
return {
...state,
currentUser: { ...payload },
isLoading: false
};
default:
return state;
}
};
推荐阅读
- python - 高斯拉普拉斯算子是用于斑点检测还是边缘检测?
- security - 限制对 Flask Web 框架中路由的访问
- asp.net - 生成 PDF 时 IIS 上的随机或乱码文本
- python-3.x - 使用情绪分析对数据进行分类
- java - EasyMock 如何捕获被测试方法调用的 void 方法的参数?
- html - 溢出-y滚动时的IE 11 bootstrap 4 flexbox宽度问题
- datetime - .csv 中的 D3.js 日期时间字段
- sql - 避免单个表的多个列的多个左连接
- c# - svcutil Generated File unity UWP 错误
- javascript - 在电子窗口之外获取选定的文本内容?