Семантический интерфейс + Реагировать на проблему с неопределенными данными - PullRequest
0 голосов
/ 08 июня 2018

Я новичок в React.У меня есть файл JSON на сервере, который получает данные через Node.JS.Я хочу присвоить первую часть этих данных selectedProfile, но там написано undefined.Когда я пытаюсь назначить, он говорит Cannot read property '*AnyProp*' of undefined ниже в части кода JSX, даже если я пытаюсь this.state.selectedProfile.general.firstName etc

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>
    );
}
}

Ответы [ 3 ]

0 голосов
/ 09 июня 2018

Ну, похоже, у вас здесь несколько проблем.В вашем 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
}

Это означало бы, что нет необходимости устанавливать состояние по умолчанию для пустого массива (которого больше нет, как вы сейчас назначаете)объект к нему, но я думаю, вы упустили это при рефакторинге для предыдущих комментариевНа самом деле, настоятельно рекомендуется оставить весь рендеринг до 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>
0 голосов
/ 04 декабря 2018

Это сработало для меня.Сделайте два разных блока визуализации.Первый render проверяет, готовы ли ваши реквизиты.Если это так, он вызывает второй с this.renderPage().Проверьте Semantic React для получения информации о Loader.

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

    renderPage() {
        //your render code here
    }
0 голосов
/ 08 июня 2018

Нельзя связываться с государством при обычном назначении.Это не сработает:

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

Вы должны перебрать свои данные и соответственно изменить свое состояние, используя setState.

...