Эмулируйте второй параметр setState в среде useState. Пусть что-то случится после того, как setState произошло - PullRequest
4 голосов
/ 29 января 2020

В предыдущей версии реагирования мы могли выполнить код после изменения состояния, выполнив что-то вроде:

setState( prevState => {myval: !prevState.myval}, () => {
    console.log("myval is changed now");
)

И убедитесь, что второй «бит» выполняется только с обновленным кодом. Теперь один из примеров описывает что-то «похожее» может быть достигнуто с помощью React.useEffect:

const [myval, setMyval] = React.useState(false);
React.useEffect(() => {
    console.log("myval is changed now");
}

//somewhere
setMyval(o => !o);

Хотя это выглядит прекрасно и модно, в моем случае это не поможет: в моем случае «какой эффект нужен произойти "зависит от того места, где состояние изменилось, изначально:

//somewhere
setState( prevState => {myval: !prevState.myval}, () => {
    console.log("myval is changed now");
)
//somewhere else 
setState( prevState => {myval: !prevState.myval}, () => {
    console.log("myval has changed somewhere else");
)

Изменение их на useState + setMyval позволит одному и тому же эффекту сработать в обеих позициях, поэтому мы не можем получить в useEffect какое действие на самом деле должно произойти.

Как бы я поступил выше в компоненте функции, основанной на перехвате?


Лучшим вариантом использования был бы экран с двумя кнопками, одна для загрузки предыдущей еще один, чтобы загрузить следующий. Кнопки отключены, пока isLoading состояние истинно и выполняет действия только при загрузке. Чтобы гарантировать, что может произойти только одна загрузка, мы не можем просто «действовать», как если бы состояние изменялось немедленно: изменение состояния происходит асинхронно, и, следовательно, может возникать состояние гонки.

Использование обратного вызова при изменении состояния предотвращает это состояние гонки и обеспечивает только одну загрузку:

class SomScreen extends React.component {
    state = { isLoading: false }
    render() {
        <div>
            <button 
                disabled={this.state.isLoading} 
                onclick={(e) => {
                    if (!this.state.isLoading) {
                            this.setState((p) => ({isLoading: true}), () => {
                            await fetchPreviousDataAndDoSomethingWithIt()
                            setState({isLoading: false});
                        });
                    }
                }
            />Previous</button>
            <button 
                disabled={this.state.isLoading} 
                onclick={(e) => {
                    if (!this.state.isLoading) {
                        this.setState((p) => ({isLoading: true}), () => {
                            await fetchNextDataAndDoSomethingWithIt()
                            setState({isLoading: false});
                        });
                    }
                }
            />Next</button>
        </div>
    }
}

1 Ответ

1 голос
/ 29 января 2020

Вопрос "является асинхронным c действие в полете" является сокращением "любого из этих асинхронных c действий в полете", так что вы можете смоделировать каждое асинхронное c действие отдельно и затем вычислить isLoading. Пользовательский хук, как это может работать:

function useAsyncAction(asyncAction) {
  const [isLoading, setIsLoading] = useState(false);
  const callAsyncAction = useCallback(async () => {
    setIsLoading(true);
    await asyncAction();
    setIsLoading(false);
  }, []);
  return [
    isLoading,
    callAsyncAction,
  ]
}

//

function SomScreen() {
  const [asyncAIsLoading, callAsyncA] =
    useAsyncAction(fetchPreviousDataAndDoSomethingWithIt);
  const [asyncBIsLoading, callAsyncB] =
    useAsyncAction(fetchNextDataAndDoSomethingWithIt);
  const isLoading = asyncAIsLoading || asyncBIsLoading

  ...
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...