BAE
BAE

Reputation: 8936

Redux/React: why `this.props` undefined

My codes:

  render() {
    console.log( 'render, state' + Array.isArray(this.props.users) + ',' + JSON.stringify(this.props.users) );
    return (
      <section>
        <div>testing</div>
        <div>{JSON.stringify(this.props.users)}</div>
        <ul>
          {this.props.users.map( user => <li>{user.email}</li>)}
        </ul>
      </section>
    );
  }

Both <div>testing</div> and <div>{JSON.stringify(this.props.users)}</div> work fine (after removing <ul>...</ul>). But <ul>...</ul> did not work. the error is:

Uncaught TypeError: Cannot read property 'map' of undefined

Any comments welcomed. Thanks

UPDATE

The following codes work fine, this.props.users is an Array, and all the console logs look OK. I just need to know why <ul>{this.props.users.map( user => <li>{user.email}</li>)}</ul> not work. Why this.props.users in <ul>...</ul> is undefined.

  render() {
    console.log( 'render, state' + Array.isArray(this.props.users) + ',' + JSON.stringify(this.props.users) );
    return (
      <section>
        <div>testing</div>
        <div>{JSON.stringify(this.props.users)}</div>
      </section>
    );
  }

the console output of the above codes (only two s):

render, statetrue,[{"_id":"5831e6df511e","updatedAt":"removed","createdAt":"removed","email":"removed","password":"xxxx","__v":0,"role":"xxx","profile":{"firstName":"test","lastName":"tester"}},{...}, {...}]

UPDATE

My codes:

import { connect } from 'react-redux';
import { fetchAllUsers } from '../../actions/index';

class HomePage extends Component {
  componentWillMount() {
    console.log( 'componentWillMount()' );
    this.props.fetchAllUsers();
  }

  render() {
    console.log( 'render(), ' + Array.isArray(this.props.users) + ',' + JSON.stringify(this.props.users) );
    return (
      <section>
        <div>{JSON.stringify(this.props.users)}</div>
      </section>
    );
  }
}

function mapStateToProps(state) {
    console.log( 'state:' + JSON.stringify(state) );// working fine, and the console.log is OK. not added to this post
    return  {
        users: state.admin.users
    }
}

export default connect(mapStateToProps, {fetchAllUsers})(HomePage);

console.log (some details in the User objects removed.):

render(), true,[{"_id":"5831","profile":{"firstName":"test","lastName":"tester"}},{"_id":"5831e874cedf511f", "profile":{"firstName":"cccc","lastName":"cc"}},{"_id":"5831120","profile":{"firstName":"cccccccc","lastName":"ccc"}}]

And, on the webpage, the string of this.props.users is shown.

My question is why <ul>{this.props.users.map( user => <li>{user.email}</li>)}</ul> not work, but <div>{JSON.stringify(this.props.users)}</div> working fine.

I think I already described my questions clearly. If more information needed, please tell me.

More details

my admin_reducer.js

import { FETCH_ALL_USERS } from '../actions/types';

const INITIAL_STATE = { users:[] };

export default function (state = INITIAL_STATE, action) {
  console.log( 'users is action.payload:'+JSON.stringify( { users:action.payload } ) );
  return Object.assign( {}, state, {users:action.payload});
  //return { ...state, users:action.payload };
}

my index.js

import adminReducer from './admin_reducer';

const rootReducer = combineReducers({
  ...
  admin: adminReducer
});

export default rootReducer;

my action.js
export function fetchAllUsers() {
  return function(dispatch) {
    axios.get(`${API_URL}/user/all`)
    .then(response => {
      dispatch({
        type: FETCH_ALL_USERS,
        payload: response.data
      });
    })
    .catch(response => dispatch(errorHandler(response.data.error)))
  }
}

UPDATE

why console log of console.log(this.props.users) is [object Object],[object Object],[object Object]?

Upvotes: 0

Views: 2330

Answers (2)

Jeff McCloud
Jeff McCloud

Reputation: 5927

You can call this without error..

JSON.stringify(this.props.users)

...because it just outputs "undefined"). That is valid. However, if you try to call a method on the undefined that was returned, of course that fails...

// Following tries to call undefined.map(), which is of course wrong
this.props.users.map( user => <li>{user.email}</li>)

Common shorthand to address your scenario uses a "short-circuit operator" (like a quickie "if (is true)")...

<ul>{this.props.user && this.props.users.map( user => <li>{user.email}</li>)}</ul>

^^ Note the "this.props.user && ", which prevents the following this.props.user.map() from being called if this.props.user is null or undefined

Upvotes: 0

Dor Weid
Dor Weid

Reputation: 454

First of, according to react docs, async requests should be made inside the componentDidMount method.

Secondly, to make sure that you're not trying to map an un-initialized array, use this:

<ul>{(this.props.users || []).map( user => <li>{user.someProperty}</li> )}</ul>

This should cover it in case you forgot to initialize the array for some reason.

Upvotes: 1

Related Questions