1
votes

I have see some answer like Cannot read property 'map' of undefined , but it is for react, not work in react-redux.

main code:

containers/TreeNode.js:

import React, { Component, PropTypes } from 'react'
import { bindActionCreators } from 'redux'
import { connect } from 'react-redux'
import classNames from 'classnames/bind'
import * as NodeActions from '../actions/NodeActions'

export default class TreeNode extends Component {

  // getInitialState() {
  //     return {nodes:[]};
  // }

  // warning.js?8a56:45 Warning: getInitialState was defined on TreeNode, a plain JavaScript class. This is only supported for classes created using React.createClass. Did you mean to define a state property instead?

  constructor(props, context) {
    super(props, context)
    this.props = {
      open: false,
      nodes: [],
      info:{}
    }
  }

  handleClick() {
    this.setState({ open: !this.state.open })
    if (this.state.open){
      this.actions.getNodes()
    }
  }

  render() {
    const { actions, nodes, info } = this.props

    console.log(this.props)
    console.log(nodes===undefined)

    return (
      <div className={classNames('tree-node', { 'open':this.props.open})} onClick={ () => {this.handleClick()} }>
        <a>{info.name}</a>
        <div>{nodes.map(node => <TreeNode info={node} />)}</div>
      </div>
    );
  }
}


TreeNode.propTypes = {
  info:PropTypes.object.isRequired,
  nodes:PropTypes.array,
  actions: PropTypes.object.isRequired
}

nodes.map would throw error cannot read property 'map' of undefined, I know nodes maybe undefined.

I have tried

  1. add getInitialState

    getInitialState() {
      return {nodes:[]};
    }
    

    Got: warning.js?8a56:45 Warning: getInitialState was defined on TreeNode, a plain JavaScript class. This is only supported for classes created using React.createClass. Did you mean to define a state property instead?

This is says getInitialState can not use in component.

  1. change render to use if else:

     render() {
        const { actions, nodes, info } = this.props
    
        console.log(this.props)
        console.log(nodes===undefined)
    
        if (nodes) {
          const children =<div>{nodes.map(node => <TreeNode info={node} />)}</div>
        } else {
          const children = <div>no open</div>
        }
    
        return (
          <div className={classNames('tree-node', { 'open':this.props.open})} onClick={ () => {this.handleClick()} }>
            <a>{info.name}</a>
            { children }
          </div>
        );
      }
    

error change to : children is undefined ....confusing.

  1. change if else condition to { ? :}

    const children = { nodes ? <div>{nodes.map(node => <TreeNode info={node} actions={actions} />)}</div> : <div>no open</div> }
    

    got syntax error:

    ERROR in ./src/containers/TreeNode.js
    Module build failed: SyntaxError: E:/Project/simple-redux-boilerplate/src/containers/TreeNode.js: Unexpected token (54:2
    9)
      52 |         // <div>{nodes.map(node => <TreeNode info={node} />)}</div>
      53 |
    > 54 |     const children = { nodes ? <div>{nodes.map(node => <TreeNode info={node} actions={actions} />)}</div> : <div>
    no open</div> }
         |                              ^
      55 |     return (
      56 |       <div className={classNames('tree-node', { 'open':this.props.open})} onClick={ () => {this.handleClick()} }>
    

And finally 4. I see some code like below in an example:

    {!user &&
    <div>
      <p>This will "log you in" as this user, storing the username in the session of the API server.</p>
    </div>
    }
    {user &&
    <div>
      <p>You are currently logged in as {user.name}.</p>
    </div>
    }

so I tried to use:

{nodes && <div>{nodes.map(node => <TreeNode info={node} />)}</div>
}

This worked , but I don't know why ..

I have a look into if-else-in-JSX, nothing related to above.

I just want to know why 2, 3 don't work, and what is the syntax 4 used, any doc?

1
By the way if you want to initialize the state in a component class, you just need to initialize it in constructor : constructor(props) { ..., this.state = {your_state}}Pierre Criulanscy

1 Answers

2
votes

I just want to know why 2, 3 don't work

2 doesn't work because consts are block-scoped. I.e. children is only accessible inside the if and else blocks. Simplified example:

if (true) {
  const foo = 42;
  // foo is only visible inside this {...}
}
console.log(foo); // error

3 doesn't work because you are using {...} in a way they are not supposed to be used. {...} are either a block or an object literal or mark an expression if used inside JSX. You are using them on the RHS of an assignment, so they are interpreted as object literal. The contents of {...} is not valid for an object literal though, hence you get a syntax error.

The why you wanted to use them only works inside JSX. Outside JSX you don't need them. This works fine:

const children = nodes ? <div>{nodes.map(node => <TreeNode info={node} actions={actions} />)}</div> : <div>no open</div>;

what is the syntax 4 used, any doc?

&& is the logical AND operator. This way works because logical AND and OR return the last evaluated value of the expression. In in your case, if nodes in array, it converts to true, which means the right operand (<div>...</div>) has to be evaluated as well.
But if nodes is undefined (converts to false), the right operand is not evaluated.

Read more about logical operators on MDN and about short circuit evaluation.

Some examples:

'' && 42    // ''
'foo' && 42 // 42

'' || 42    // 42
'foo' || 42 // 'foo'