在异步调用之前更新状态的正确方法

Posted

技术标签:

【中文标题】在异步调用之前更新状态的正确方法【英文标题】:Correct Way to Update Sate before Async call 【发布时间】:2022-01-23 07:32:12 【问题描述】:

我正在尝试更新状态以在进行异步调用时显示 ActivityIndi​​cator。

确保IsLoading(true)setResult(null) 在异步调用之前发生的正确方法是什么?

在下面的代码中,setIsLoading 在异步调用之前实际上并未更新,因此 ActivityIndi​​cator 不会出现在 UI 上。我意识到为什么会出现这种情况 (here) 我只是不知道在进行异步调用之前实际确保 ActivityIndi​​cator 显示的推荐方法。

请帮忙。在反应中做到这一点的正确方法是什么?

const MyScreen = () => 
    const [isLoading, setIsLoading] = useState(false);
    const [result, setResult] = useState(null);

    const handlePress = async () => 

        setResult(null);  // reset result
        setIsLoading(true);  // show activity indicator

        const result = await MyAsyncFunc();  // native modules function
        setResult(result);
        setIsLoading(false);  // turn off activity indicator

    

    return (
        <View style=styles.container>
            <MyButton title="Run" handlePress=handlePress disabled=isLoading />
            isLoading && <ActivityIndicator size="large" color="#00ff00" />
            <Text>result</Text>
        </View>
    )


const styles = StyleSheet.create(
    container: 
        flex: 1,
        alignItems: 'center',
        justifyContent: 'center'
    ,
)

【问题讨论】:

【参考方案1】:

尝试添加useEffect 并在更改result 依赖项时设置isLoading false,如下所示:

const MyScreen = () => 
    const [isLoading, setIsLoading] = useState(false);
    const [result, setResult] = useState(null);

    const handlePress = async () => 

        setResult(null);  // reset result
        setIsLoading(true);  // show activity indicator

        const result = await MyAsyncFunc();  // native modules function
        setResult(result);
        //setIsLoading(false);  // turn off activity indicator

    

    useEffect(() => 
        if(result && isLoading)
            setIsLoading(false);
        
    , [isLoading, result])

    return (
        <View style=styles.container>
            <MyButton title="Run" handlePress=handlePress disabled=isLoading />
            isLoading && <ActivityIndicator size="large" color="#00ff00" />
            <Text>result</Text>
        </View>
    )

希望这对你有用

【讨论】:

感谢您的回复,但我在运行此代码时看到了同样的问题。我认为 idiglove 上面的回答通过将 MyAsyncFunc 放入 useEffect that listens to changes on isLoading. This guarantees that isLoading` 将在 MyAsyncFunc 运行之前更改,从而解决了这个问题。【参考方案2】:

您可以尝试只使用一种状态吗?我认为不需要isLoading。 试试这个

!result && <ActivityIndicator size="large" color="#00ff00" />

编辑:让我们利用 useEffect 来监听 isLoading 是否为真

...
useEffect(() => 
 async function load ()
  if (isLoading) 
   const result = await MyAsyncFunc();  // native modules function
   setResult(result);
   setIsLoading(false);  // turn off activity indicator
  
 
 load()
, [isLoading])

const handlePress = () => 
   setResult(null);  // reset result
   setIsLoading(true);  // show activity indicator

【讨论】:

当用户第一次访问屏幕时,result 将为空,我会喜欢显示的 ActivityIndi​​cator。他们会按下按钮来触发异步功能。 感谢您的编辑。这样可行!我学到了一些东西:D

以上是关于在异步调用之前更新状态的正确方法的主要内容,如果未能解决你的问题,请参考以下文章

Redux-thunk 异步调用和状态

异步数据asyncData调用请求后端接口

在单元测试中同步调用异步方法是不是不正确?

异步firebase调用后SwiftUI不更新变量

异步设置 ReactJS 状态

如何正确调用 Parallel.ForEach 循环中的调用异步方法[重复]