简体   繁体   English

React Native:将 useState() 数据传递给不相关的屏幕

[英]React Native: Passing useState() data to unrelated screens

Explanation: I am creating a fitness app, my fitness app has a component called WorkoutTimer that connects to the workout screen, and that screen is accessed via the HomeScreen.说明:我正在创建一个健身应用程序,我的健身应用程序有一个名为 WorkoutTimer 的组件,它连接到锻炼屏幕,该屏幕可通过 HomeScreen 访问。 Inside the WorkoutTimer, I have an exerciseCount useState() that counts every time the timer does a complete loop (onto the next exercise).在 WorkoutTimer 中,我有一个 executionCount useState(),它在每次计时器完成一个完整循环时进行计数(进入下一个练习)。 I have a different screen called StatsScreen which is accessed via the HomeScreen tab that I plan to display (and save) the number of exercises completed.我有一个名为 StatsScreen 的不同屏幕,可通过我计划显示(并保存)已完成练习的数量的 HomeScreen 选项卡访问。

What I've done: I have quite literally spent all day researching around this, but it seems a bit harder with unrelated screens.我所做的:我真的花了一整天的时间来研究这个问题,但对于不相关的屏幕似乎有点困难。 I saw I might have to use useContext() but it seemed super difficult.我看到我可能必须使用 useContext() 但它似乎非常困难。 I am fairly new to react native so I am trying my best haha!我对本地反应还很陌生,所以我正在尽我所能哈哈! I have attached the code for each screen I think is needed, and attached a screenshot of my homeScreen tab so you can get a feel of how my application works.我附上了我认为需要的每个屏幕的代码,并附上了我的 homeScreen 选项卡的屏幕截图,以便您了解我的应用程序是如何工作的。

WorkoutTimer.js锻炼计时器.js

import React, { useState, useEffect, useRef } from "react";
import {
  StyleSheet,
  Text,
  View,
  TouchableOpacity,
  Button,
  Animated,
  Image,
  SafeAreaView,
} from "react-native";

import { CountdownCircleTimer } from "react-native-countdown-circle-timer";
import { Colors } from "../colors/Colors";

export default function WorkoutTimer() {
  const [count, setCount] = useState(1);
  const [exerciseCount, setExerciseCount] = useState(0);
  const [workoutCount, setWorkoutCount] = useState(0);

  const exercise = new Array(21);
  exercise[1] = require("../assets/FR1.png");
  exercise[2] = require("../assets/FR2.png");
  exercise[3] = require("../assets/FR3.png");
  exercise[4] = require("../assets/FR4.png");
  exercise[5] = require("../assets/FR5.png");
  exercise[6] = require("../assets/FR6.png");
  exercise[7] = require("../assets/FR7.png");
  exercise[8] = require("../assets/FR8.png");
  exercise[9] = require("../assets/S1.png");
  exercise[10] = require("../assets/S2.png");
  exercise[11] = require("../assets/S3.png");
  exercise[12] = require("../assets/S4.png");
  exercise[13] = require("../assets/S5.png");
  exercise[14] = require("../assets/S6.png");
  exercise[15] = require("../assets/S7.png");
  exercise[16] = require("../assets/S8.png");
  exercise[17] = require("../assets/S9.png");
  exercise[18] = require("../assets/S10.png");
  exercise[19] = require("../assets/S11.png");
  exercise[20] = require("../assets/S12.png");
  exercise[21] = require("../assets/S13.png");

  return (
    <View style={styles.container}>
      <View style={styles.timerCont}>
        <CountdownCircleTimer
          isPlaying
          duration={45}
          size={240}
          colors={"#7B4FFF"}
          onComplete={() => {
            setCount((prevState) => prevState + 1);
            setExerciseCount((prevState) => prevState + 1);

            if (count == 21) {
              return [false, 0];
            }
            return [(true, 1000)]; // repeat animation for one second
          }}
        >
          {({ remainingTime, animatedColor }) => (
            <View>
              <Image
                source={exercise[count]}
                style={{
                  width: 150,
                  height: 150,
                }}
              />
              <View style={styles.timeOutside}>
                <Animated.Text
                  style={{
                    color: animatedColor,
                    fontSize: 18,
                    position: "absolute",
                    marginTop: 67,
                    marginLeft: 35,
                  }}
                >
                  {remainingTime}
                </Animated.Text>
                <Text style={styles.value}>seconds</Text>
              </View>
            </View>
          )}
        </CountdownCircleTimer>
      </View>
    </View>
  );
}

const styles = StyleSheet.create({})
    

WorkoutScreen.js锻炼屏幕.js

import React, { useState } from "react";
import { StyleSheet, Text, View } from "react-native";

import WorkoutTimer from "../components/WorkoutTimer";

export default function WorkoutScreen() {
  return (
    <View style={styles.container}>
      <WorkoutTimer />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: "#fff",
    alignItems: "center",
    justifyContent: "center",
  },
});
    

HomeScreen.js主屏幕.js

import React from "react";
import { StyleSheet, Text, View, SafeAreaView, Button } from "react-native";
import { TouchableOpacity } from "react-native-gesture-handler";
import { AntDesign } from "@expo/vector-icons";

import { Colors } from "../colors/Colors";

export default function HomeScreen({ navigation }) {
  return (
    <SafeAreaView style={styles.container}>
      <Text style={styles.pageRef}>SUMMARY</Text>
      <Text style={styles.heading}>STRETCH & ROLL</Text>
      <View style={styles.content}>
        <TouchableOpacity
          style={styles.timerDefault}
          onPress={() => navigation.navigate("WorkoutScreen")}
        >
          <Button title="START WORKOUT" color={Colors.primary} />
        </TouchableOpacity>
        <TouchableOpacity
          style={styles.statContainer}
          onPress={() => navigation.navigate("StatsScreen")}
        >
          <AntDesign name="barschart" size={18} color={Colors.primary} />
          <Text style={{ color: Colors.primary }}>Statistics</Text>
          <AntDesign name="book" size={18} color={Colors.primary} />
        </TouchableOpacity>
      </View>
    </SafeAreaView>
  );
}

const styles = StyleSheet.create({})
    

StatsScreen.js StatsScreen.js

import React from "react";
import { StyleSheet, Text, View } from "react-native";
import { exerciseCount, workoutCount } from "../components/WorkoutTimer";

export default function StatsScreen() {
  return (
    <View style={styles.container}>
      <Text display={exerciseCount} style={styles.exerciseText}>
        {exerciseCount}
      </Text>
      <Text display={workoutCount} style={styles.workoutText}>
        {workoutCount}
      </Text>
    </View>
  );
}

const styles = StyleSheet.create({});

Home Screen Image主屏幕图像

As far as I can tell, you're almost there!据我所知,你快到了! You're trying to get your 2 state variables from the WorkoutTimer like this:您正在尝试从WorkoutTimer获取 2 个状态变量,如下所示:

import { exerciseCount, workoutCount } from "../components/WorkoutTimer";

Unfortunatly this won't work :( . These two variables change throughout your App's life-time and that kinda makes them "special" .不幸的是,这行不通:(。这两个变量在您的应用程序的整个生命周期中都会发生变化,这使它们变得“特别”

In React, these kinds of variables need to be declared in a parent component and passed along to all children , which are interested in them.在 React 中,这些类型的变量需要在父组件中声明并传递给所有对它们感兴趣的子组件 So in your current Setup you have a parent child relationship like:因此,在您当前的设置中,您有一个父子关系,例如:

HomeScreen -> WorkoutScreen -> WorkoutTimer.

If you move the variables to HomeScreen (HomeScreen.js)如果将变量移动到 HomeScreen (HomeScreen.js)

export default function HomeScreen({ navigation }) {
  const [exerciseCount, setExerciseCount] = useState(0);
  const [workoutCount, setWorkoutCount] = useState(0);

you can then pass them along to WorkoutScreen or StatsScreen with something like:然后,您可以将它们传递给WorkoutScreenStatsScreen ,例如:

navigation.navigate("WorkoutScreen", { exerciseCount })
navigation.navigate("StatsScreen", { exerciseCount })

You'll probably have to read up on react-navigation's documentation for .navigate I'm not sure I remember this correctly.您可能需要阅读关于.navigate react-navigation 文档,我不确定我是否记得正确。

In order to read the variable you can then:为了读取变量,您可以:

  export default function WorkoutScreen({ navigation }) {
    const exerciseCount  = navigation.getParam(exerciseCount);

    return (
      <View style={styles.container}>
        <WorkoutTimer exerciseCount={exerciseCount} />
      </View>
    );
  }

and finally show it in the WorkoutTimer:最后在 WorkoutTimer 中显示:

export default function WorkoutTimer({ exerciseCount }) {

Of course that's just part of the solution, since you'll also have to pass along a way to update your variables ( setExerciseCount and setWorkoutCount ).当然,这只是解决方案的一部分,因为您还必须传递一种更新变量的方法( setExerciseCountsetWorkoutCount )。

I encourage you to read through the links I posted and try to get this to work.我鼓励您通读我发布的链接并尝试使其正常工作。 After you've accumulated a few of these stateful variables, you might also want to look at Redux , but this is a bit much for now.在您积累了一些这些有状态变量之后,您可能还想看看Redux ,但现在这有点多。

Your app looks cool, keep at it!你的应用看起来很酷,坚持下去!

I ended up solving this problem with useContext if anyone is curious, it was hard to solve initially.如果有人好奇,我最终用 useContext 解决了这个问题,最初很难解决。 But once I got my head around it, it wasn't too difficult to understand.但是一旦我理解了它,就不难理解了。

I created another file called exerciseContext with this code:我使用以下代码创建了另一个名为 executionContext 的文件:

import React, { useState, createContext } from "react";

const ExerciseContext = createContext([{}, () => {}]);

const ExerciseProvider = (props) => {
  const [state, setState] = useState(0);
  //{ exerciseCount: 0, workoutCount: 0 }

  return (
    <ExerciseContext.Provider value={[state, setState]}>
      {props.children}
    </ExerciseContext.Provider>
  );
};

export { ExerciseContext, ExerciseProvider };

and in App.js I used ExerciseProvider which allowed me to pass the data over the screens.在 App.js 中,我使用了 ExerciseProvider,它允许我在屏幕上传递数据。

  if (fontsLoaded) {
    return (
      <ExerciseProvider>
        <NavigationContainer>
          <MyTabs />
        </NavigationContainer>
      </ExerciseProvider>
    );
  } else {
    return (
      <AppLoading startAsync={getFonts} onFinish={() => setFontsLoaded(true)} />
    );
  }
}

I could call it with:我可以这样称呼它:

import { ExerciseContext } from "../components/ExerciseContext";

and

const [exerciseCount, setExerciseCount] = useContext(ExerciseContext);

This meant I could change the state too!这意味着我也可以改变状态! Boom, solved!砰,解决了! If anyone needs an explanation, let me know!如果有人需要解释,请告诉我!

I think you have to use Mobx or Redux for state management.我认为您必须使用 Mobx 或 Redux 进行状态管理。 That will be more productive for you instead built-in state.这对你来说会更有效率,而不是内置状态。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM