React Native:将 useState() 数据传递给不相关的屏幕
React Native: Passing useState() data to unrelated screens
说明:我正在创建一个健身应用程序,我的健身应用程序有一个名为 WorkoutTimer 的组件,它连接到锻炼屏幕,并且可以通过主屏幕访问该屏幕。在 WorkoutTimer 中,我有一个 exerciseCount useState(),它会在每次计时器执行完整循环(进入下一个练习)时计数。我有一个名为 StatsScreen 的不同屏幕,可通过 HomeScreen 选项卡访问,我计划显示(并保存)已完成的练习数。
我所做的:我确实花了一整天的时间来研究这个问题,但对于不相关的屏幕来说似乎有点困难。我看到我可能不得不使用 useContext() 但这似乎非常困难。我对原生反应还很陌生,所以我正在尽我最大的努力哈哈!我附上了我认为需要的每个屏幕的代码,并附上了我的主屏幕选项卡的屏幕截图,以便您了解我的应用程序的工作原理。
WorkoutTimer.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
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
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
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
据我所知,您快完成了!你试图让你的 2 状态
来自 WorkoutTimer
的变量如下所示:
import { exerciseCount, workoutCount } from "../components/WorkoutTimer";
不幸的是,这行不通:( 。这两个变量在您的整个过程中都会发生变化
应用程序的 life-time 有点 makes them "special"。
在React中,这些变量需要在parent组件中声明
并传递给所有对它们感兴趣的 children。
因此,在您当前的设置中,您有一个 parent child 关系,例如:
HomeScreen -> WorkoutScreen -> WorkoutTimer.
如果将变量移动到主屏幕 (HomeScreen.js)
export default function HomeScreen({ navigation }) {
const [exerciseCount, setExerciseCount] = useState(0);
const [workoutCount, setWorkoutCount] = useState(0);
然后您可以将它们传递给 WorkoutScreen
或 StatsScreen
喜欢:
navigation.navigate("WorkoutScreen", { exerciseCount })
navigation.navigate("StatsScreen", { exerciseCount })
您可能需要阅读 react-navigation 的 .navigate
文档我不确定我是否记得正确。
为了读取变量,您可以:
export default function WorkoutScreen({ navigation }) {
const exerciseCount = navigation.getParam(exerciseCount);
return (
<View style={styles.container}>
<WorkoutTimer exerciseCount={exerciseCount} />
</View>
);
}
最后在 WorkoutTimer 中显示:
export default function WorkoutTimer({ exerciseCount }) {
当然这只是解决方案的一部分,因为您还必须通过
沿途更新您的变量(setExerciseCount
和 setWorkoutCount
)。
我鼓励您通读我发布的链接并尝试让它发挥作用。
在你积累了一些这样的有状态变量之后,你可能还想看看 Redux,但现在这有点多了。
你的应用看起来很酷,坚持下去!
如果有人好奇,我最终用 useContext 解决了这个问题,最初很难解决。但是一旦我理解了它,它就不难理解了。
我使用以下代码创建了另一个名为 exerciseContext 的文件:
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 };
在 App.js 中,我使用了 ExerciseProvider,它允许我在屏幕上传递数据。
if (fontsLoaded) {
return (
<ExerciseProvider>
<NavigationContainer>
<MyTabs />
</NavigationContainer>
</ExerciseProvider>
);
} else {
return (
<AppLoading startAsync={getFonts} onFinish={() => setFontsLoaded(true)} />
);
}
}
我可以这样调用它:
import { ExerciseContext } from "../components/ExerciseContext";
和
const [exerciseCount, setExerciseCount] = useContext(ExerciseContext);
这意味着我也可以改变状态!轰,解决了!如果有人需要解释,请告诉我!
我认为你必须使用 Mobx 或 Redux 进行状态管理。这对你来说会更有效率 built-in state.
说明:我正在创建一个健身应用程序,我的健身应用程序有一个名为 WorkoutTimer 的组件,它连接到锻炼屏幕,并且可以通过主屏幕访问该屏幕。在 WorkoutTimer 中,我有一个 exerciseCount useState(),它会在每次计时器执行完整循环(进入下一个练习)时计数。我有一个名为 StatsScreen 的不同屏幕,可通过 HomeScreen 选项卡访问,我计划显示(并保存)已完成的练习数。
我所做的:我确实花了一整天的时间来研究这个问题,但对于不相关的屏幕来说似乎有点困难。我看到我可能不得不使用 useContext() 但这似乎非常困难。我对原生反应还很陌生,所以我正在尽我最大的努力哈哈!我附上了我认为需要的每个屏幕的代码,并附上了我的主屏幕选项卡的屏幕截图,以便您了解我的应用程序的工作原理。
WorkoutTimer.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
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
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
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
据我所知,您快完成了!你试图让你的 2 状态
来自 WorkoutTimer
的变量如下所示:
import { exerciseCount, workoutCount } from "../components/WorkoutTimer";
不幸的是,这行不通:( 。这两个变量在您的整个过程中都会发生变化 应用程序的 life-time 有点 makes them "special"。
在React中,这些变量需要在parent组件中声明 并传递给所有对它们感兴趣的 children。 因此,在您当前的设置中,您有一个 parent child 关系,例如:
HomeScreen -> WorkoutScreen -> WorkoutTimer.
如果将变量移动到主屏幕 (HomeScreen.js)
export default function HomeScreen({ navigation }) {
const [exerciseCount, setExerciseCount] = useState(0);
const [workoutCount, setWorkoutCount] = useState(0);
然后您可以将它们传递给 WorkoutScreen
或 StatsScreen
喜欢:
navigation.navigate("WorkoutScreen", { exerciseCount })
navigation.navigate("StatsScreen", { exerciseCount })
您可能需要阅读 react-navigation 的 .navigate
文档我不确定我是否记得正确。
为了读取变量,您可以:
export default function WorkoutScreen({ navigation }) {
const exerciseCount = navigation.getParam(exerciseCount);
return (
<View style={styles.container}>
<WorkoutTimer exerciseCount={exerciseCount} />
</View>
);
}
最后在 WorkoutTimer 中显示:
export default function WorkoutTimer({ exerciseCount }) {
当然这只是解决方案的一部分,因为您还必须通过
沿途更新您的变量(setExerciseCount
和 setWorkoutCount
)。
我鼓励您通读我发布的链接并尝试让它发挥作用。 在你积累了一些这样的有状态变量之后,你可能还想看看 Redux,但现在这有点多了。
你的应用看起来很酷,坚持下去!
如果有人好奇,我最终用 useContext 解决了这个问题,最初很难解决。但是一旦我理解了它,它就不难理解了。
我使用以下代码创建了另一个名为 exerciseContext 的文件:
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 };
在 App.js 中,我使用了 ExerciseProvider,它允许我在屏幕上传递数据。
if (fontsLoaded) {
return (
<ExerciseProvider>
<NavigationContainer>
<MyTabs />
</NavigationContainer>
</ExerciseProvider>
);
} else {
return (
<AppLoading startAsync={getFonts} onFinish={() => setFontsLoaded(true)} />
);
}
}
我可以这样调用它:
import { ExerciseContext } from "../components/ExerciseContext";
和
const [exerciseCount, setExerciseCount] = useContext(ExerciseContext);
这意味着我也可以改变状态!轰,解决了!如果有人需要解释,请告诉我!
我认为你必须使用 Mobx 或 Redux 进行状态管理。这对你来说会更有效率 built-in state.