首页 > 解决方案 > TypeError:无法读取 null 的属性“名称”与 redux 反应

问题描述

每次我离开一个用户并想输入另一个用户时都会发生错误。

当我自动登录时,用户和用户的个人资料都被清除了,所以我不知道每次更改用户时都会出现什么问题。

如果我删除“Clear_user”,当我登录并再次登录时,新用户仍然拥有前一个用户的信息,直到我刷新页面。

重新加载页面后,此错误停止出现。

https://imgur.com/83tk94E

组件/perfil/Perfil.js

import React, {useEffect, Fragment, useState} from 'react';
import PropTypes from 'prop-types';
import {getCurrentProfile, deleteAccount} from '../../actions/profile';
import {connect} from 'react-redux';
import {logout} from '../../actions/auth';
import Spinner from '../layout/Spinner';
import {Link, withRouter} from 'react-router-dom';
import InputSelector from '../util/InputSelector';
import {getUser, editUser} from '../../actions/user';

const Perfil = ({ user:{user,loading}, getUser, editUser, deleteAccount, getCurrentProfile, profile: {profile}, logout,
    history }) => {
        const [formData, setFormData] = useState({
            name: '',
            email: '',
            avatar:''
        });

        useEffect(()=>{
            getCurrentProfile();
            getUser();
            setFormData({
                name: loading || !user.name ? '' : user.name,
                email: loading || !user.email ? '' : user.email,
                avatar: loading || !user.avatar ? '' : user.avatar,
            });
        }, [loading]);

        const {
            name,
            email,
            avatar
        } = formData;

    const hasPerfil = (
        <div>hola</div>
    );

    const hasNotPerfil = (
        <div className="text-center">
            <p>Aun no has establecido tu perfil, porfavor agrega algo de información.</p>
            <Link to="/crear-perfil" className="btn btn-primary my-1"> Crear un perfil</Link>
        </div>
      );

      const onSubmit = async e => {
        e.preventDefault();
        editUser(formData, history, true);
        }; 

      const onChange = e => setFormData({...formData,[e.target.name]: e.target.value});
    return loading && profile === null ? <Spinner/> : <Fragment>
            <div className="container">
                <div className="card user-con-col mb-4">
                    <div className="card-body">
                        <div className="d-flex align-items-center justify-content-between">
                        <h5 className="card-title mb-0"><i className="fas fa-user"></i> Usuario: {user && user.name}</h5>
                        <button onClick={logout} className="btn btn-info my-1"><i class="fas fa-sign-out-alt"></i> Salir</button>
                        </div>
                        </div>
                </div>
                <div className="card user-con-col mb-4">
                    <div className="card-body user-con-col-1">
                        <div className="container">
                        <div className="row">
                        <div className="col-md-12 text-center mb-3">
                            <div className="card">
                                    <div className="card-body">
                                    <div className="Botones">
                        <Link className="btn btn-primary my-1 mr-1" to="/perfiles"><i class="fas fa-border-all"></i> Más perfiles</Link>
                        <Link className="btn btn-primary my-1 mr-1" to="/editar-perfil"><i className="fas fa-pen"></i> Editar Perfil</Link>
                        </div>
                                    </div>
                            </div>
                        </div>
                        {profile !== null ? hasPerfil : hasNotPerfil }
                        <div className="col-md-12 mb-3">
        <div className="card">
            <div className="card-body">
                <div className="row">
                    <div className="col-md-3 d-flex align-items-center">
                    <div className="img">
                    <img className="img-fluid" src={avatar} />
                </div>
                    </div>
                <div className="col-md-9">
                <form onSubmit={e => onSubmit(e)}>
                    <div className="form-group">
                        <label><i className="fas fa-user"></i> Username</label>
                        <input 
                        type="text" 
                        name="name"
                        className="form-control" 
                        placeholder="Edita tu nombre de usuario"
                        value={name}
                        onChange={e => onChange(e)}
                        />
                    </div>
                    <div className="form-group">
                        <label><i className="fas fa-envelope"></i> Email</label>
                        <input 
                        type="text" 
                        name="email"
                        className="form-control" 
                        placeholder="Edita tu email"
                        value={email}
                        onChange={e => onChange(e)}
                        />
                    </div>
                    <div className="form-group" >
                    <label><i class="fas fa-upload"></i> Imagen De Perfil</label>
                <InputSelector/>
                </div>
                <div className="col-md-12 text-center">
                <button className="btn btn-primary btn-block"><i class="fas fa-check"></i> Guardar</button>
                </div>
                </form> 
                </div>
                </div>
            </div>
        </div>
    </div>
                        <div className="col-md-12 text-center">
                            <div className="card">
                                    <div className="card-body">
                                    <div className="Botones">
                        <button onClick={() => deleteAccount()} className="btn btn-danger my-1 mr-1"><i class="fas fa-sign-out-alt"></i> Borrar cuenta</button>
                        </div>
                                    </div>
                            </div>
                        </div>
                        </div>
                        </div>
                    </div>
                    </div>
            </div>
        </Fragment>;
};

Perfil.propTypes = {
    getCurrentProfile: PropTypes.func.isRequired,
    logout: PropTypes.func.isRequired,
    deleteAccount: PropTypes.func.isRequired,
    profile: PropTypes.object.isRequired,
    editUser:PropTypes.func.isRequired,
    getUser: PropTypes.func.isRequired,
    user: PropTypes.object.isRequired
}

const mapStateToProps = state =>({
    profile: state.profile,
    user: state.user
});

export default connect(mapStateToProps, {getCurrentProfile, getUser, editUser, logout, deleteAccount})(withRouter(Perfil));

减速器/user.js

import {GET_USER, USER_ERROR, CLEAR_USER, UPDATE_USER} from '../actions/types';

const initialState = {
    user: null,
    loading: true,
    error: {}
}

export default function(state = initialState, action) {
    const {type, payload} = action;
    switch(type){
        case GET_USER:
        case UPDATE_USER:
        return{
            ...state,
            loading:false,
            user:payload
        };
        case USER_ERROR:
            return{
            ...state,
            error:payload,
            loading: false
        };
        case CLEAR_USER:
            return {
            ...state,
            loading:false,
            user:null
        }
        default:
            return state;
    }
}

动作/user.js

import axios from 'axios';
import {setAlert} from './alert';

import {GET_USER, USER_ERROR} from './types';

//Get current users profile
export const getUser = () => async dispatch => {
    try {
        const res = await axios.get('/api/users/me');

        dispatch({
            type: GET_USER,
            payload: res.data
        });
    } catch (err) {
        dispatch({
            type:USER_ERROR,
            payload:{msg: err.response.statusText, status: err.response.status}
        });
    }
};

//Create or update user
export const editUser = (formData, history, edit = false) => async dispatch => {
    try {
        const config = {
            headers: {
                'Content-Type': 'application/json',
            }
        }

        const res = await axios.post('/api/usersedit', formData, config);

        dispatch({
            type: GET_USER,
            payload: res.data
        });

        dispatch(setAlert(edit ? 'User update' : 'User created', 'success'));

        if(!edit){
            history.push('/perfil');
        }
    } catch (err) {
        const errors = err.response.data.errors;
        if(errors){
            errors.forEach(error => dispatch(setAlert(error.msg, 'danger')));
        }
        dispatch({
            type:USER_ERROR,
            payload:{msg: err.response.statusText, status: err.response.status}
        });
    }
};

route

/api/users.js

const express = require('express');
const router = express.Router();
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');
const config = require('config');
const {check, validationResult} = require('express-validator');
const auth = require('../../middlewares/auth');
const User = require('../../models/User');

//@Route    GET api/users
//@desc     Test route
//@access   Public
router.get('/', (req, res) => res.send('User Route'));

//@Route    GET api/users/me
//@desc     Get Current User
//@access   Private
router.get('/me', auth, async (req, res) => {
    try {
        const user = await User.findOne({_id: req.user.id});
        if(!user){
            return res.status(400).json({msg: 'There is no user'});
        }
        res.json(user);
    } catch (err) {
        console.error(err.message);
        res.status(500).send('Server Error');
    }
});

//@Route    POST api/users
//@desc     Register user
//@access   Public
router.post('/', [
    check('name', 'Name is required').not().isEmpty(),
    check('email', 'Please enter an email').isEmail(),
    check('password', 'Please enter a password with 6 or more characters').isLength({min: 6})
    ], async (req, res) => {
    const errors = validationResult(req);
    if(!errors.isEmpty()){
        return res.status(400).json({errors: errors.array()});
    }

    const {name, password, email} = req.body;

    //See if user exists
    try {
        let user = await User.findOne({email});

        if(user){
            return res.status(400).json({errors: [{msg: 'User already exists'}]});
        }

    user = new User({
        name,
        email,
        avatar:'/uploads/noImg.jpg',
        password
    });

    //Encrypt password
    const salt = await bcrypt.genSalt(10);

    user.password = await bcrypt.hash(password, salt);

    await user.save();

    //Return jsonwebtoken
    const payload = {
        user:{
            id: user.id
        }
    };

    jwt.sign(payload, config.get('jwtSecret'),{expiresIn: 360000}, (err, token) =>{
        if(err) throw err;
        res.json({token});
        }
    );

    } catch (err) {
        console.error(err.message);
        res.status(500).send('Server error');
    }

});

module.exports = router;

似乎是因为名称对象为空或null,但我找不到解决方案,请帮助。

标签: javascriptreactjsredux

解决方案


问题将在异步函数和加载状态。

loadingin store 最初设置为 true。在第一次渲染中就可以了。仅评估条件的第一部分 ( ),跳过 loading第二部分 ( )。我认为您需要在等待异步响应时设置回 true 。这通常通过以下方式完成:!user.nameloading

  1. 在调用异步函数之前设置loading为 true(同步)(这部分缺失 - 您需要另一个动作和动作类型)。
  2. 从异步函数获得响应后,您设置loading为 false(您已经这样做了)。
 useEffect(()=>{
            getCurrentProfile();
           // this is asynchronous function, the user is not loaded right after you call getUser.
           // Hence the user from props could be not initalized (still set to null).
           // This is why you are getting that error in the next lines (`setFormData`).
            getUser();
            setFormData({
                name: loading || !user.name ? '' : user.name,
                email: loading || !user.email ? '' : user.email,
                avatar: loading || !user.avatar ? '' : user.avatar,
            });
        }, [loading]);

推荐阅读