ReactJS: как правильно управлять состоянием в моем случае

#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. посмотрите, что я добавил к своему ответу