2016-01-30 44 views
2

我想使用json返回的內容不僅填充我的列表,而且創建一個新的列表以允許選擇一個父項。reactjs - 未捕獲TypeError:無法讀取未定義的屬性'地圖'

我名單即將在我的導航,但是當我點擊「創建新的列表」它提供了以下錯誤:

Uncaught TypeError: Cannot read property 'map' of undefined 

其中關於這段代碼特定部分發生:

render: function() { 
    var navNodes = this.props.data.map(function(nav) { 
    return (
     React.createElement(NavItems, {name: nav.name, key: nav.id}) 
    ); 
    }); 
    return (
     React.createElement(Input, { 
      label: "Parent List", 
      ref: "input", 
      value: this.state.value, 
      bsStyle: this.state.validationState, 
      hasFeedback: true, 
      help: this.state.hint, 
      onChange: this.handleChange, 
      type: "select"}, 
      this.renderPlaceholder(), 
      navNodes 
     ) 
    ); 

代碼:

var Bootstrap = ReactBootstrap; 
var Input = ReactBootstrap.Input; 

var NavItems = React.createClass({ 
    render: function() { 
    return (
     <option value={1}>{navNodes}</option> 
    ); 
    } 
}); 

var NavItem = React.createClass({ 
    render: function() { 
    return (
     <li><a href="#">{this.props.name}</a></li> 
    ); 
    } 
}); 

var NavList = React.createClass({ 
    render: function() { 
    var navNodes = this.props.data.map(function(nav) { 
     return (
     <NavItem name={nav.name} key={nav.id}></NavItem> 
    ); 
    }); 
    return (
     <ul className="nav"> 
     <li className="current"><a href="#"><i className="glyphicon glyphicon-home"></i> Lists</a></li> 
     {navNodes} 
     </ul> 
    ); 
    } 
}); 

var NewListButton = React.createClass({ 
    render: function() { 
    return (
     <a {...this.props} 
     href="javascript:;" 
     role="button" 
     className={(this.props.className || '') + ' btn'} 
     /> 
    ); 
    } 
}); 

var ListNameInput = React.createClass({ 
    getInitialState: function() { 
     return { 
      value: '', 
      hint: null, 
      validationState: null 
     }; 
    }, 
    handleChange: function() { 
     var newValue = this.refs.input.getValue(), 
      hint = null, 
      validationState = null, 
      length = newValue.length; 
     if (length > 2) { 
      validationState = 'success'; 
     } else { 
      validationState = 'error'; 
      if (length) { 
       hint = 'The name must be at least 3 characters long.'; 
      } else { 
       hint = 'This value is required.'; 
      } 
     } 
     this.setState({ 
      value: newValue, 
      hint: hint, 
      validationState: validationState 
     }); 
    }, 
    isValid: function() { 
     return ('success' === this.state.validationState); 
    }, 
    render: function() { 
     return (
      <Input 
       label='Name' 
       ref='input' 
       value={this.state.value} 
       placeholder='Enter List Name' 
       bsStyle={this.state.validationState} 
       hasFeedback 
       help={this.state.hint} 
       onChange={this.handleChange} 
       type='text' /> 
     ); 
    } 
}); 

var ListDescriptionInput = React.createClass({ 
    getInitialState: function() { 
     return { 
      value: '', 
      hint: null, 
      validationState: null 
     }; 
    }, 
    handleChange: function() { 
     var newValue = this.refs.input.getValue(), 
      hint = null, 
      validationState = null, 
      length = newValue.length; 
     if (length > 2) { 
      validationState = 'success'; 
     } else { 
      validationState = 'error'; 
      if (length) { 
       hint = 'The description must be at least 3 characters long.'; 
      } else { 
       hint = 'This value is required.'; 
      } 
     } 
     this.setState({ 
      value: newValue, 
      hint: hint, 
      validationState: validationState 
     }); 
    }, 
    isValid: function() { 
     return ('success' === this.state.validationState); 
    }, 
    render: function() { 
     return (
      <Input 
       label='Description' 
       ref='input' 
       value={this.state.value} 
       placeholder='Enter Description' 
       bsStyle={this.state.validationState} 
       hasFeedback 
       help={this.state.hint} 
       onChange={this.handleChange} 
       type='text' /> 
     ); 
    } 
}); 

var WidgetListSelect = React.createClass({ 
    getInitialState: function() { 
     return { 
      value: 0, 
      hint: null, 
      validationState: null 
     }; 
    }, 
    handleChange: function() { 
     var newValue = Number(this.refs.input.getValue()), 
      hint = null, 
      validationState = null; 
     if (0 === newValue) { 
      validationState = 'error'; 
      hint = 'You must select an parent list.'; 
     } else { 
      validationState = 'success'; 
     } 
     this.setState({ 
      value: newValue, 
      hint: hint, 
      validationState: validationState 
     }); 
    }, 
    isValid: function() { 
     return ('success' === this.state.validationState); 
    }, 
    renderPlaceholder: function() { 
     if (0 !== this.state.value) 
      return null; 
     // Show placeholder only prior to a value being selected 
     return (
      <option value={0}> 
       Please select a Parent List 
      </option> 
     ); 
    }, 
    render: function() { 
     var navNodes = this.props.data.map(function(nav) { 
     return (
      <NavItems name={nav.name} key={nav.id}></NavItems> 
     ); 
     }); 
     return (
      <Input 
       label='Parent List' 
       ref='input' 
       value={this.state.value} 
       bsStyle={this.state.validationState} 
       hasFeedback 
       help={this.state.hint} 
       onChange={this.handleChange} 
       type='select'> 
       {this.renderPlaceholder()} 
       {navNodes} 
      </Input> 
     ); 
    } 
}); 

var CreateNewListForm = React.createClass({ 
    handleSubmit: function() { 
     var isValid = 
      !!(this.refs.nameInput.isValid() 
       & this.refs.descriptionInput.isValid() 
       & this.refs.widgetlistSelect.isValid()); 
     if (isValid) { 
      this.props.closeModal(); 
     } else { 
      // Force validation of each element to show errors to user 
      this.refs.nameInput.handleChange(); 
      this.refs.descriptionInput.handleChange(); 
      this.refs.widgetlistSelect.handleChange(); 
     } 
    }, 
    render: function() { 
     return (
      <div> 
       <ListNameInput 
        ref='nameInput' /> 
       <ListDescriptionInput 
        ref='descriptionInput' /> 
       <WidgetListSelect 
        ref='widgetlistSelect' /> 
       <Bootstrap.ButtonToolbar> 
        <Bootstrap.Button 
         onClick={this.handleSubmit}> 
         Save 
        </Bootstrap.Button> 
        <Bootstrap.Button 
         onClick={this.props.closeModal}> 
         Cancel 
        </Bootstrap.Button> 
       </Bootstrap.ButtonToolbar> 
      </div> 
     ); 
    } 
}); 

var NavBox= React.createClass({ 
    loadNavsFromServer: function() { 
    $.ajax({ 
     url: "http://servername/api/widgetlists/?format=json", 
     dataType: 'json', 
     cache: false, 
     success: function(data) { 
     this.setState({data: data}); 
     }.bind(this), 
     error: function(xhr, status, err) { 
     console.error("http://servername/api/widgetlists/?format=json", status, err.toString()); 
     }.bind(this) 
    }); 
    }, 
    handleListSubmit: function(comment) { 
    // TODO: submit to the server and refresh the list 
    }, 
    getInitialState: function() { 
    return { 
     modalVisible: false, 
     data: []}; 
    }, 
    onClick: function() { 
     this.setState({modalVisible: true}); 
    }, 
    hideModal: function() { 
     this.setState({modalVisible: false}); 
    }, 
    renderModal: function() { 
     return (
      <Bootstrap.Modal 
      show={this.state.modalVisible} 
      onHide={this.hideModal}> 
       <Bootstrap.Modal.Body> 
        <CreateNewListForm 
        closeModal={this.hideModal} /> 
       </Bootstrap.Modal.Body> 
      </Bootstrap.Modal> 
    ); 
    }, 
    componentDidMount: function() { 
    this.loadNavsFromServer(); 
    }, 
    render: function() { 
    return (
     <div className="col-md-2"> 
     <div className="sidebar content-box" style={{display: "block"}}> 
      <NavList data={this.state.data} /> 
      {this.renderModal()} 
      <Bootstrap.Button onClick={this.onClick}> 
       Create New List 
      </Bootstrap.Button> 
     </div> 
     </div> 
    ); 
    } 
}); 

module.exports = { 
    NavBox: NavBox 
} 
+1

所以'this.props.data'顯然不是一個數組,你有沒有嘗試登錄它,看看它到底是什麼 – adeneo

+1

嘗試登錄了'render'調用,它可能是在道具準備好之前多次調用'this.props.data'可能是不確定的,這會導致這個錯誤。我還推薦使用[lodash](http://lodash.com)的'_.map',這是類型安全的。 –

回答

4

您需要返回值getDefaultProps

https://facebook.github.io/react/docs/reusable-components.html#default-prop-values

getDefaultProps: function() { 
    return { 
     data: [] 
    }; 
    } 

此外,這是很好的做法,定義你的道具種類太多:

propTypes: { 
    data: React.PropTypes.array.isRequired 
} 

雖然我會避免ES6/7類糖因爲我不是一個風扇規範(對於你的JS頭腦來說不健康,如糖),但如果你喜歡它,那麼他們也可以將它們定義爲static屬性defaultProps

class Foo extends React.Component { 
    static defaultProps = { 
    bar: React.PropTypes.array.isRequired 
    } 
} 

https://facebook.github.io/react/blog/2015/01/27/react-v0.13.0-beta-1.html#es7-property-initializers

1

this.props.data沒有被定義在第一時間render()首先被調用,並且您試圖在undefined上調用.map(),這會導致錯誤。

您應該在調用映射之前檢查數據是否已定義,否則將navNode設置爲空數組。

const data = this.props.data; 
    const navNodes = data ? data.map(...your function...) : []; 
相關問題