#reactjs
#reactjs
Вопрос:
Итак, у меня есть контейнер (упрощенная версия), он работает следующим образом:
- Шаг 1: получите версию конфигурации
- Шаг 2: прочитайте приведенный выше конфигурационный файл из S3, используя версию из
- Шаг 3: после загрузки версии установите флаг isLoadLiveConfigComplete -> true, чтобы я знал, что могу отображать
- Шаг 4: настройте набор вкладок для отображения в зависимости от того, какая из них нажата
- Шаг 5: визуализация определенной вкладки
Проблема в состоянии.проблемы на шаге 5 не определены, когда я пытаюсь отобразить вкладку. Но я проверил, что на шаге 3 у него уже есть содержимое.
Итак, я прочитал, что setState работает асинхронно, что означает, что я не могу использовать его последовательно. Но я проверил эти данные на шаге 3 и установил state.challenges, и я не отображаю вкладки, пока флаг не будет установлен. Почему это все еще не работает? Есть предложения о том, как это исправить в этом случае?
версия 2 — использовались обратные вызовы в 2 местах:
class CLL_AppContainer extends Component{
constructor(props) {
super(props);
this.state = { // initial state
tab:CLL_Constants.TABNAME_CHALLANGES,
isLoadLoginDataComplete:false,
isLoadLiveConfigComplete: false,
challenges: [],
configVersion: ""
};
}
componentDidMount() { // loads into browser page
this.getLiveName(); // step1: get the configVersion
…
}
onGetLiveName(data){
// step2: use a callback here to make sure readFromS3() is called after configVersion is set first
this.setState({liveConfig:data.version}, () => {this.readFromS3(this.state.configVersion)});
}
readFromS3(version){
var form = new FormData();
…
form.append('configName', this.state.liveConfig);
…
// step3: read the config file from S3
// fetchAjax() accepts a callback for when the response.success to further process the returned data
fetchAjax(form, (data) => {
console.log(data.configString); // yup I can see the data loaded from S3
this.setState({challenges: data.configString.types});
this.setState({isLoadLiveConfigComplete: true} );
})
}
render() {
var state = this.state;
if(!state.isLoadLoginDataComplete amp;amp; !state.isLoadLiveConfigComplete) {
return <Spinner />
}
// step4: setup bunch of tabs to render depending on which one gets clicked
return (
<div>
<div align="center">
<button type="button" className="btn btn-default" onClick={() => this.setState({tab: CLL_Constants.TABNAME_CHALLANGES})}>
{CLL_Constants.TABNAME_CHALLANGES}
</button>
…
</div>
<div>
{this.renderTab()}
</div>
</div>
);
}
renderTab() {
var challenges = this.state.challenges; // console.log() shows this is undefined, why???
switch (this.state.tab)
{
case CLL_Constants.TABNAME_CHALLANGES:
return (
<CLL_ChallangesContainer
challenges={challenges}
…
</CLL_ChallangesContainer>
);
…
default:
return (
<Spinner />
);
}
}
}
Комментарии:
1. Можете ли вы
console.log(data.configString.types)
зайтиonReadFromS3
внутрь и убедиться, что оно не определено?2. хммм, я думал, что сделал это, и у data.configString.types есть содержимое, но это не похоже на правду;-(
3. @larz: Я внес некоторые изменения в код, теперь data.configString содержит содержимое, возвращенное из S3. Однако state.challenges все еще не определено;-(
4. Попробуйте объединить оба ваших
setState
вызоваfetchAjax
в один вызов. Нет причин разделять их, и посколькуsetState
является асинхронным, я думаю, что, скорее всего, флаг устанавливается перед данными.5. @lawrence-witt:
fetchAjax
это универсальная функция, которую я использовал в нескольких разных местах, принимая форму и обратный вызов при успешном вызове Ajax и получении действительного ответа. Обратные вызовы различаются в зависимости от того, где я его использовал.
Ответ №1:
Действительно, есть способ «принудительной синхронизации» при использовании setState. Этого можно достичь с помощью обратного вызова setState:
this.setState({.... }, () => { *do after the setState is done* })
Некоторые люди говорят, что это может быть трудно понять, но
() => { *do after the setState is done* }
это просто функция, которую вы могли бы передать в качестве значения для переменной. Разберитесь в этой части, и весь мир React будет у вас под рукой!
Примечание: При использовании упомянутого обратного вызова будьте также осторожны при использовании «this».
Попробуйте также выполнить : …..
const self = this
fetchAjax(form, (data) => {
console.log(data.configString); // yup I can see the data loaded from S3
self.setState({challenges: data.configString.types, isLoadLiveConfigComplete: true});
})
….
Будьте осторожны с использованием «этого»
Комментарии:
1. немного изменил код, используя 2 обратных вызова, так что теперь data.configString на шаге 3 имеет содержимое. Однако state.challenges все еще не определен.
2. посмотрите, что я добавил к своему ответу