How to render properties of objects in React?

2019-09-08 02:55发布

问题:

Where exactly should I deal with the problem of this component not loading with the desired state?

My render method causes the following error...

Uncaught TypeError: Cannot read property 'email' of undefined

...even though the JSON.stringify line shows me that the email property does (eventually) exist.

The console.log down in mapStateToProps confirms that state loads first without the any user property (thus causing the error).

Behold my naive attempt to resolve this in my constructor method. It's not working.

What is the right way to deal with this situation? Some conditional inside the render method? Tried that too but still no luck.

import React, {Component, PropTypes} from 'react';
import {connect} from 'react-redux';
import * as actions from '../actions';

class Feature extends Component {

    constructor(props){
        super(props);
        this.state = {
            'auth': {
                'user':{
                    email:'',
                    id:''
                }
            }
        }
    }

    componentWillMount() {
        this.props.fetchMessage();        // puts the user object into state
    }

    render() {
        return (
            <div className="feature">
                Here is your feature
                {JSON.stringify(this.props.user , null, 2)}
                {this.props.user.email}
            </div>
        );
    }

}

function mapStateToProps(state) {
    console.log('state',state);
    return { user: state.auth.user }
}

export default connect(mapStateToProps, actions)(Feature);

/////////// action /////////

export function fetchMessage(){

    return function(dispatch){
        axios
            .get(ROOT_URL, {
                headers: {
                    authorization: localStorage.getItem('token')
                }
            })
            .then((response) => {
                dispatch({
                    type: FETCH_MESSAGE,
                    payload: response.data.user
                })
            })
    }

}

///////////////// reducer /////////////

var authReducer = (state={}, action) => {
    console.log('action.payload',action.payload);

    switch(action.type){
        case AUTH_USER: return      {...state, error: '', authenticated: true};
        case UNAUTH_USER: return    {...state, error: '', authenticated: false};
        case AUTH_ERROR: return     {...state, error: action.payload};
        case FETCH_MESSAGE: return  {...state, user: {
                                                        email: action.payload.email,
                                                        id: action.payload._id
                                                    }};
        default: return state;
    };
};

回答1:

Redux uses a global state called store that lives outside your component.

Inside your constructor you have a this.state = {...} statement. This is a local state that is only available to the Feature component.

connect from react-redux basically connects the info inside the store to your component props hence called mapStateToProps.

Edit your mapStateToProps to something like...

function mapStateToProps(state) {
   const { auth: { user = {} } = {}} = state;
   return {
      user
   }
}

What it does is try to extract the user property from the store and if it doesn't exist yet, set it to empty object. Your error is due to your mapStateToProps accessing the user property that doesn't exist yet. You might want to set a default value as your initialState to avoid this issue.



回答2:

So here is what is happening.. You are making a server request for a user object and on success the received object is stored in your redux store. While performing such an action your component is rendered as follows:

  1. You have initiated the request to the server, which means currently there is no user in your store and so the component is rendered with this.props.user undefined.
  2. Your request is successful and the user object is stored in your store. When this happens react-redux will re-render your component with the user object and this.props.user is available in your component.

    During the first step since this.props.user is unavailable you are getting an error while accessing this.props.user.email. Although @jpdelatorre 's answer is pretty good, another thing you can do is simply add a check in your render method.

     render() {
         let user = this.props.user || {};
         ....
             {user.email}
         ....
     }
    


标签: redux