【问题标题】:Semantic UI + React issue with undefined data语义 UI + 未定义数据的反应问题
【发布时间】:2018-11-18 22:29:02
【问题描述】:

我对 React 还是很陌生。我在服务器上有 JSON 文件,通过 Node.JS 从中获取数据。我想将此数据的第一部分分配给selectedProfile,但它显示undefined。当我尝试分配它时,即使我尝试 this.state.selectedProfile.general.firstName 等,它也会在代码的 JSX 部分中显示 Cannot read property '*AnyProp*' of undefined 较低

import React from 'react';
import ReactDOM from 'react-dom';
import { Image, Search, Grid, Menu } from 'semantic-ui-react';

class ContentFeed extends React.Component {


constructor() {
    super();
    this.state = {
        'items': [],
        'prevName': '',
        'i': 0,
        'selectedProfile': []
    }
    this.getItems = this.getItems.bind(this);

}

componentWillMount() {
    this.getItems();
}


getItems() {

    var jsonData = [];

    const xhr = new XMLHttpRequest();

    xhr.onload = () => {
        if (xhr.status === 200) {
            jsonData = JSON.parse(xhr.responseText);
            this.setState({'items': jsonData});
            this.setState({'prevName': jsonData[0].general.firstName + ' ' + jsonData[0].general.lastName});
            document.getElementById(this.state.prevName).style = 'border: 3px solid black';
            this.setState({'selectedProfile': this.state.items[0]});
            console.log(jsonData);
        } else {
            console.log('Error: No 200 OK response');
        }
    }

    xhr.open('get', 'http://localhost:8000/api/item');
    xhr.send();

}

handleItemClick = (e, { name }) => {
    if (name !== this.state.prevName) {
        document.getElementById(name).style = 'border: 3px solid black';
        document.getElementById(this.state.prevName).style = 'border: 1px solid black';
        this.setState({'prevName': name});
        let i = this.state.items.findIndex(element => {
            return (element.general.firstName + ' ' + element.general.lastName) === name;
        });
        this.setState({'i': i});

        this.setState({'selectedProfile': this.state.items[i]});
    }
}

render() {
    return (
        <Grid>
            <Grid.Column width={4} style={{'height': '700px', 'overflowY': 'scroll'}}>
                <Search />              
                    {this.state.items.map( (item, index) => {
                    return (
                        <Menu key={index} fluid vertical tabular>
                            <Menu.Item 
                                key={item.general.firstName + ' ' + item.general.lastName}
                                name={item.general.firstName + ' ' + item.general.lastName} 
                                id={item.general.firstName + ' ' + item.general.lastName} 
                                onClick={this.handleItemClick}
                                style={{border: '1px solid black'}}
                            >
                            <Image src={item.general.avatar} style={{'width': '50px', 'height': '50px'}}/>
                            <p><br />{item.general.firstName + ' ' + item.general.lastName}</p>
                            <p>{item.job.title}</p>
                            </Menu.Item>
                        </Menu>
                    )
                })}
            </Grid.Column>

            <Grid.Column stretched width={12}>
              <div style={{'margin': 'auto'}} id="content">
                    <h2 style={{'marginTop': '10px', 'overflow': 'auto'}}>{this.state.selectedProfile[0]} {this.state.selectedProfile[1]}</h2>
                    <p></p>
                    <Image style={{'margin': '10px', 'float': 'left'}} src={this.state.selectedProfile[2]}/>
                    <p>Company: {this.state.selectedProfile[3]}</p>
                    <p>Title: {this.state.selectedProfile[4]}</p><br />
                    <p>Email: {this.state.selectedProfile[5]}</p>
                    <p>Phone: {this.state.selectedProfile[6]}</p>
                    <p>Address: {this.state.selectedProfile[7]}</p>
                    <p>City: {this.state.selectedProfile[8]}</p>
                    <p>ZIP: {this.state.selectedProfile[9]}</p>
                    <p>Country: {this.state.selectedProfile[10]}</p>
              </div>
            </Grid.Column>
        </Grid>
    );
}
}

【问题讨论】:

  • 硬编码索引看起来很糟糕,为什么不添加一些数据来指示相关元素的标签。另请注意,您的 selectedProfile 将在首次加载时为空,getitems 应该位于componentWillMount() 生命周期事件、XmlHttpRequest vs fetch 或 axios 或类似的库中,就问题而言,输出 jsondata 会很有帮助(例如为什么jsondata中的第一项)
  • @Icepickle 仍未定义
  • @Icepickle jsonData: 21 个这样的单元:0 :{general: {…}, job: {…}, contact: {…}, address: {…}}

标签: javascript reactjs jsx semantic-ui-react


【解决方案1】:

这对我有用。制作两个不同的渲染块。第一个render 检查你的道具是否准备好了。如果是这样,它用this.renderPage() 调用第二个。查看Semantic React 以获取有关Loader 的信息。

    render() {
        return (this.props.ready) ? this.renderPage() : <Loader active>Getting data</Loader>;
    }

    renderPage() {
        //your render code here
    }

【讨论】:

    【解决方案2】:

    好吧,您似乎在这里遇到了多个问题。在您的 getItems() xhr 回调中,您正在执行以下操作

    jsonData = JSON.parse(xhr.responseText);
    this.setState({'items': jsonData});
    this.setState({'prevName': jsonData[0].general.firstName + ' ' + jsonData[0].general.lastName});
    this.setState({'selectedProfile': this.state.items[0]});
    

    但是this.setState 是一个异步函数,这意味着在设置状态时,您不能假设this.state 已经更新。为了论证起见,如果您在状态中设置多个值,请随意将它们组合在一个语句中,例如:

    this.setState({ 
      items: jsonData, 
      selectedProfile: jsonData[0],
      prevName: jsonData[0].general.firstName + ' ' + jsonData[0].general.lastName
    });
    

    但是,这不是这里唯一的问题,而是您似乎遇到的数据流问题。您应该考虑到您在第一次渲染时还没有数据。所以为了处理这个问题,你应该在你的渲染函数内部检查,如果实际上有一个selectedProfile,然后再尝试渲染它的任何属性。所以在这种情况下,你可以像这样改变渲染:

    render() {
      const { items, selectedProfile } = this.props;
      if (!selectedProfile) {
        // render nothing till the selected profile is there
        return null;
      }
      // the rest of your rendering to return the grid (you can now use items & selectedProfile without the prefix this.state
    }
    

    这意味着不需要将默认状态设置为空数组(现在不再是,因为您现在正在为其分配一个对象,但我猜您在重构以前的 cmets 时错过了这一点)

    constructor() {
      super();
      this.state = {
        items: [],
        selectedProfile: null
      };
    }
    

    您的代码中的一个重要注意事项是 DOM 操作,您不应该通过选择一个项目并对其进行变异的传统方式进行 DOM 操作,事实上,强烈建议将所有渲染都留给 React。在那里,classNames 库可以帮助您进行有条件的className 选择。

    您当前的this.handleItemClick 实现也将暂时丢失this 的上下文,您可以选择在构造函数中绑定handleItemClick(就像您对getItems 所做的那样,那个是不需要),或者使用事件处理程序的箭头函数。

    所以,如果所有这些点都改变了,你会想出一个类似的代码(请注意,我曾经使用过 api,因为它易于使用并且有一个我可以写入的公共 api)

    const Dog = ( { name, content, isSelected, onSelected } ) => {
      return <div onClick={() => onSelected(name)} className={ classNames('breed', {'selected': isSelected}) }>{ name }</div>;
    };
    
    class DogList extends React.Component {
      constructor() {
        super();
        this.state = {
          loading: true,
          selectedBreed: null
        };
      }
      componentWillMount() {
        this.fetchDogs();
      }
      componentDidUmount() {
        this.mounted = false;
      }
      selectBreed( breedName ) {
        this.setState( { selectedBreed: breedName } );
      }
      fetchDogs() {
        fetch('https://dog.ceo/api/breeds/list/all')
          .then( response => response.json() )
          .then( json => {
            this.setState( { breeds: json.message, loading: false } );
          })
          .catch( err => console.error( err ) );
      }
      render() {
        const { loading, breeds, selectedBreed } = this.state;
        if ( loading ) {
          return <div>Loading dogs list</div>;
        }
        return <div className="dogs">{ Object.keys( breeds ).map( key => <Dog key={key} name={key} content={breeds[key]} isSelected={selectedBreed === key} onSelected={(...args) => this.selectBreed(...args)} /> ) }</div>;
      }
    }
    
    const target = document.querySelector('#container');
    ReactDOM.render( <DogList />, target );
    .breed {
      margin: 3px;
      padding: 5px;
    }
    .selected {
      border: solid #a0a0a0 1px;
      background-color: #00ff00;
    }
    <script id="react" src="https://cdnjs.cloudflare.com/ajax/libs/react/15.6.2/react.js"></script>
    <script id="react-dom" src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/15.6.2/react-dom.js"></script>
    <script id="classnames" src="https://cdnjs.cloudflare.com/ajax/libs/classnames/2.2.5/index.js"></script>
    <div id="container"></div>

    【讨论】:

      【解决方案3】:

      你不能通过正常的赋值来搞乱状态。这行不通:

      this.state.selectedProfile[0] = jsonData[0].general.firstName;

      您应该循环访问您的数据并使用setState 相应地更改您的状态。

      【讨论】:

      • 仍未定义
      • 没问题,就这样
      猜你喜欢
      • 2018-12-07
      • 2019-12-26
      • 1970-01-01
      • 2019-01-22
      • 2018-09-17
      • 2018-08-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多