我试图将一个值保存到异步存储,然后根据异步存储的值结果导航到正确的页面.我可以将数据存储在AsyncStorage中,但我的状态不会更新,我必须重新加载应用程序才能更新状态.这是我的代码:

这里有一个欢迎/反对屏幕.我希望此屏幕仅显示给新的应用程序用户.因此,当用户按下"继续"按钮时,我希望将一个值保存到异步存储中,以便下次登录时,他们不必再次看到登录页面.这是我的入职页面:

const WelcomeScreen: FC<IWelcomeScreen> = ({ navigation }) => {
  const { width, height } = Dimensions.get("window");

  const btnText = "Contiunue";
  const title = "Book";
  const subTitle = "Fab";

  let [fontsLoaded] = useFonts({
    PinyonScript_400Regular,
  });

  const continueBtn = async () => {
    try {
      await AsyncStorage.setItem('@viewedOnboarding', 'true');
    } catch (error) {
      console.log('Error @setItem: ', error);
    };
  };

  if (!fontsLoaded) {
    return <Text>...Loading</Text>;
  } else {
    return (
      <View style={containerStyle(height, width).container}>
        <ImageBackground
          resizeMode={"cover"}
          style={styles.image}
          source={require("../assets/model.jpg")}
        >
          <LinearGradient
            colors={["#00000000", "#000000"]}
            style={styles.gradient}
          >
            <View style={styles.container}>
              <View style={styles.logoTextContainer}>
                <Text style={styles.logoText}>{title}</Text>
                <Text style={styles.logoText}>{subTitle}</Text>
              </View>

              <ContinueBtn label={btnText} callback={continueBtn} />
            </View>
          </LinearGradient>
        </ImageBackground>
      </View>
    );
  }
};

在我的AppNavigator中,我想决定用户应该看到哪个导航.但当我按下"继续"页面时,我的应用程序不会导航到我的TabsNavigator.它保留在我的入职页面上,但如果我刷新应用程序,则应用程序将导航到我的选项卡导航器.下面是我确定用户应该在哪里的代码,这取决于他们是新用户还是"旧"用户:

const WelcomeScreen: FC<IWelcomeScreen> = ({ navigation }) => {
  const { width, height } = Dimensions.get("window");

  const btnText = "Contiunue";
  const title = "Book";
  const subTitle = "Fab";

  let [fontsLoaded] = useFonts({
    PinyonScript_400Regular,
  });

  const continueBtn = async () => {
    try {
      await AsyncStorage.setItem('@viewedOnboarding', 'true');
    } catch (error) {
      console.log('Error @setItem: ', error);
    };
  };

  if (!fontsLoaded) {
    return <Text>...Loading</Text>;
  } else {
    return (
      <View style={containerStyle(height, width).container}>
        <ImageBackground
          resizeMode={"cover"}
          style={styles.image}
          source={require("../assets/model.jpg")}
        >
          <LinearGradient
            colors={["#00000000", "#000000"]}
            style={styles.gradient}
          >
            <View style={styles.container}>
              <View style={styles.logoTextContainer}>
                <Text style={styles.logoText}>{title}</Text>
                <Text style={styles.logoText}>{subTitle}</Text>
              </View>

              <ContinueBtn label={btnText} callback={continueBtn} />
            </View>
          </LinearGradient>
        </ImageBackground>
      </View>
    );
  }
};

推荐答案

在异步存储中设置值不会触发AppNavigator的重新加载.因此,如果用户按下continue button,那么视觉上将不会发生任何事情,因为AppNavigator的状态没有改变.如果刷新应用程序,则在初始渲染时,先前使用setItem函数设置的标志将在AppNavigator中重新加载.这就是为什么它在刷新应用程序后工作的原因.

对于这种问题,我建议您使用Context来触发AppNavigator中的状态更改.

下面是一个关于如何工作的最小示例.我在代码中添加了注释来指导您.

为了简单起见,我们将做出以下假设:

我们在一个堆栈中有两个屏幕,一个是WelcomeScreen,另一个叫做HomeScreen.

请注意,我们根据应用程序上下文对屏幕使用条件呈现.你可以添加任何你想要的屏幕,甚至整个导航器(如果你的导航器是嵌套的,这是必要的,但模式保持不变).

App

export const AppContext = React.createContext()

const App = () => {
  // it is important that the initial state is undefined, since
  // we need to wait for the async storage to return its value 
  // before rendering anything
  const [hasViewedOnboarding, setHasViewedOnboarding] = React.useState()

  const appContextValue = useMemo(
    () => ({
      hasViewedOnboarding,
      setHasViewedOnboarding,
    }),
    [hasViewedOnboarding]
  )

  // retrieve the onboarding flag from the async storage in a useEffect
  React.useEffect(() => {
       const init = async () => {
          const value = await AsyncStorage.getItem('@viewedOnboarding')
          setHasViewedOnboarding(value != null ? JSON.parse(value) : false)
       }
       init()
  }, [])

  // as long as the flag has not been loaded, return null
  if (hasViewedOnboarding === undefined) {
    return null
  }

  // wrap everything in AppContext.Provider an pass the context as a value
  return (
      <AppContext.Provider value={appContextValue}>
        <NavigationContainer>
           <Stack.Navigator>
             {!hasViewedOnboarding ? (
                <Stack.Screen name="Welcome" component={WelcomeScreen} />
              ) : (
                <Stack.Screen
                  name="Home"
                  component={HomeScreen}
                />
              )}}
           </Stack.Navigator>
        </NavigationContainer>
     </AppContext.Provider>
  )
}

现在,在您的WelcomeScreen中,您需要访问上下文并在存储异步值后设置状态.

const WelcomeScreen: FC<IWelcomeScreen> = ({ navigation }) => {
  
  // access the context

  const { setHasViewedOnboarding } = useContext(AppContext)
   
  const { width, height } = Dimensions.get("window");

  const btnText = "Contiunue";
  const title = "Book";
  const subTitle = "Fab";

  let [fontsLoaded] = useFonts({
    PinyonScript_400Regular,
  });

  const continueBtn = async () => {
    try {
      await AsyncStorage.setItem('@viewedOnboarding', 'true');
      setHasViewedOnboarding(true)
    } catch (error) {
      console.log('Error @setItem: ', error);
    };
  };

  if (!fontsLoaded) {
    return <Text>...Loading</Text>;
  } else {
    return (
      <View style={containerStyle(height, width).container}>
        <ImageBackground
          resizeMode={"cover"}
          style={styles.image}
          source={require("../assets/model.jpg")}
        >
          <LinearGradient
            colors={["#00000000", "#000000"]}
            style={styles.gradient}
          >
            <View style={styles.container}>
              <View style={styles.logoTextContainer}>
                <Text style={styles.logoText}>{title}</Text>
                <Text style={styles.logoText}>{subTitle}</Text>
              </View>

              <ContinueBtn label={btnText} callback={continueBtn} />
            </View>
          </LinearGradient>
        </ImageBackground>
      </View>
    );
  }
};

Typescript相关问答推荐

类型脚本如何将嵌套的通用类型展开为父通用类型

您可以创建一个类型来表示打字员吗

如何从TypScript中的接口中正确获取特定键类型的所有属性?

将props传递给子组件并有条件地呈现它''

我用相同的Redux—Toolkit查询同时呈现两个不同的组件,但使用不同的参数

使用ngrok解析Angular 时出现HTTP故障

具有动态键的泛型类型

使用axios在ngOnInit中初始化列表

TS2339:类型{}上不存在属性消息

跟踪深度路径时按条件提取嵌套类型

为什么S struct 类型化(即鸭子类型化)需要非严格类型联合?

在正常函数内部调用异步函数

TypeScrip-如何自动推断UNION变量的类型

VUE 3类型';字符串';不可分配给类型';参考<;字符串&>

任何导航器都未处理有效负载为";params";:{";roomId";:";...";}}的导航操作

如何使用useSearchParams保持状态

通过函数传递确切的类型,但验证额外的字段

在类型{}上找不到带有string类型参数的索引签名 - TypeScript

使用本机 Promise 覆盖 WinJS Promise 作为 Chrome 扩展内容脚本?

可选通用映射器函数出错