2016-01-30 3 views
2

Я пытаюсь использовать содержимое json return для заполнения не только моего списка, но и при создании нового списка, чтобы разрешить выбор родителя.reactjs - Uncaught TypeError: Не удается прочитать свойство 'map' of undefined

У меня есть список придумывают в моей навигации, но когда я нажимаю на «Создать новый список» дает следующее сообщение об ошибке:

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', что приведет к этой ошибке. Я также рекомендую использовать '_.map' [lodash] (http://lodash.com), который безопасен по типу. –

ответ

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 property 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(), и вы пытаетесь вызвать .map() на undefined, что приводит к ошибке.

Вы должны проверить, что данные определены до вызова карты на нем, и в противном случае установите navNodes в пустой массив.

const data = this.props.data; 
    const navNodes = data ? data.map(...your function...) : []; 
Смежные вопросы