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