I need to save the ID of a field from the settings.tsx view. That value need to be used with other view, like stations.tsx, alarms.tsx, map.tsx.
If later, I need to watch another field, I can change the field from a drop menu and the new value must be used from the other field. It meens, I should be able to print the select ID filed from the other View, but it does not!
First I renamed /hook/fieldContext.ts to /hook/filedProvider.tsx. I needed to add ts*x* to avoid error message at FieldContext.Provider
.
I copied a suggestion as the following
import React, { createContext, useState, ReactNode } from 'react';
type FieldContextType = {
field: string;
setField: (value: string) => void;
};
export const FieldContext = createContext<FieldContextType | undefined>(undefined);
export const FieldProvider = ({ children }: { children: ReactNode }) => {
const [field, setField] = useState("-1");
return (<FieldContext.Provider value={{ field, setField }}>
{children}
</FieldContext.Provider>);
};
Q1: I suppose, the above code is not a hook any more because of tsx. Should I move it to constants or components ?
Secondly: It has been asked to add <FieldProvider></FieldProvider>
at the root of my application, where is the navigation. I am confuse.
Let me first introduce to my structure to better understand my problem
app
- (tab)
-- map
--- layout.tsx
--- [id].tsx
-- settings
--- layout.tsx
--- setting.tsx
--- info.tsx
-- stations.tsx
-- index.tsx
-- alarms.tsx
-- layout.tsx (add here)
- layout.tsx (or add here?)
My navigation look like
app/layout.tsx
import { Stack } from "expo-router";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
const queryClient = new QueryClient
export default function Layout() {
return (
<QueryClientProvider client={queryClient}>
<Stack>
<Stack.Screen
name="(tabs)"
options={{
headerShown: false
}}
/>
<Stack.Screen
name="+not-found"
/*
options={{
headerShown: false,
}}
*/
/>
</Stack>
</QueryClientProvider>
);
}
and app/(tab)/layout.tsx
import FontAwesome from '@expo/vector-icons/FontAwesome6';
import { Tabs} from 'expo-router';
import { StyleSheet } from "react-native";
import { useThemeColors } from '@/hooks/useThemeColors';
import { useNavigation } from '@react-navigation/native';
import { useState } from "react";
import { FieldProvider } from '@/hooks/fieldProvider';
export default function TabLayout() {
const colors= useThemeColors()
const navigation = useNavigation();
const [sortKey, setSortKey] = useState<"id_station" | "station_name">("id_station")
return (
<Tabs screenOptions={{ tabBarActiveTintColor: 'green'}}>
<Tabs.Screen
name="index"
options={{
headerShown: false,
title: 'Home',
headerStyle: {backgroundColor: colors.appHeadBackgroundColor, borderColor:'red'},
headerTintColor: '#000',
headerTitleStyle: {
fontWeight: 'normal',
},
tabBarIcon: ({ color }) => <FontAwesome size={28} name="arrows-to-circle" color={color} />,
tabBarStyle: {
display:'none',
backgroundColor: colors.appTabBarBackgroundColor,
paddingTop:8,
height:70,
}
}}
/>
<Tabs.Screen
name="map"
options={{
title: 'Map',
headerShown: false,
headerStyle: { backgroundColor: colors.appHeadBackgroundColor},
headerTintColor: '#000',
headerTitleStyle: {
fontWeight: 'normal',
},
tabBarIcon: ({ color }) => <FontAwesome size={28} name="map-location-dot" color={color} />,
//headerTitle: props => <LogoTitle />,
tabBarStyle: {
backgroundColor: colors.appTabBarBackgroundColor,
//paddingTop:8,
height:70,
}
}}
/>
<Tabs.Screen
name="station"
options={{
headerStyle: {
backgroundColor: colors.appHeadBackgroundColor
},
headerTintColor: '#000',
headerTitleStyle: {
fontWeight: 'normal',
},
headerShown: false,
href: null,
tabBarIcon: ({ color }) => <FontAwesome size={28} name="chart-line" color={color} />,
tabBarStyle: {
//backgroundColor: colors.appTabBarBackgroundColor,
//paddingTop:8,
height:70,
}
}}
/>
<Tabs.Screen
name="alarms"
options={{
title: 'Alarmes',
headerShown: false,
headerStyle: { backgroundColor: colors.appHeadBackgroundColor},
headerTintColor: '#000',
headerTitleStyle: {
fontWeight: 'normal',
},
/*
headerRight: () => (
<Button onPress={() => router.back()}>Back</Button>
),
*/
tabBarIcon: ({ color }) => <FontAwesome size={28} name="bell" color={color} />,
//headerTitle: props => <LogoTitle />,
tabBarStyle: {
//backgroundColor: colors.appTabBarBackgroundColor,
//paddingTop:8,
height:70,
//borderWidth:30,
}
}}
/>
<Tabs.Screen
name="stations"
options={{
title: 'Stations',
headerShown: false,
headerStyle: { backgroundColor: colors.appHeadBackgroundColor},
headerTintColor: '#000',
headerTitleStyle: {
fontWeight: 'normal',
},
tabBarIcon: ({ color }) => <FontAwesome size={28} name="map-pin" color={color} />,
tabBarStyle: {
//backgroundColor: colors.appTabBarBackgroundColor,
height:70,
}
}}
/>
<Tabs.Screen
name="settings"
options={{
title: 'Paramètres',
headerShown: false,
headerStyle: { backgroundColor: colors.appHeadBackgroundColor },
headerTintColor: '#000',
headerTitleStyle: {
fontWeight: 'normal',
},
/*
headerRight: () => (
<Button onPress={() => router.back()}>Back</Button>
),
*/
tabBarIcon: ({ color }) => <FontAwesome size={28} name="gear" color={color} />,
//headerTitle: props => <LogoTitle />,
tabBarStyle: {
backgroundColor: colors.appTabBarBackgroundColor,
//paddingTop:8,
height:70,
}
}}
/>
</Tabs>
);
}
I was not really sure if <FieldProvider>
should go to at app/layout.tsx or at app/(tab)/layout.tsx (I tried both)
Problem: How can I get the value in my view settings.tsx and map.tsx. I tried the following in my view alarms.tsx (it need it as well) as that view is very simple at the moment.
As <FieldProvider>
is in app/layout.tsx, I tried the following, but the selected field is not printed, {filed}
shows an error
import { Image, View } from "react-native";
import { useContext, useEffect, useState } from "react";
import { RootView } from "@/components/RootView";
import { ThemedText } from "@/components/ThemedText";
import { Row } from "@/components/Row";
import css from "@/hooks/styles";
import { useThemeColors } from "@/hooks/useThemeColors";
import AsyncStorage from '@react-native-async-storage/async-storage';
import { imagesHeader } from "@/components/imagesHeader";
import { FieldContext, FieldProvider } from "@/hooks/fieldProvider";
export default function Alarms() {
const colors = useThemeColors()
const field = useContext(FieldContext)
return(
<RootView>
<View style={[{backgroundColor: colors.appHeadBackgroundColor }]}>
<View style={css.jumbotronContainer}>
<Image
style={css.jumbotronImage}
source={imagesHeader.alarms.uri}
/>
</View>
</View>
<View style={css.container}>
<Row>
<ThemedText>Terrain sélectionner: </ThemedText>
<ThemedText>{field}</ThemedText>
</Row>
<ThemedText style={[css.text,css.paragraphe]}>Aucune station en alarme</ThemedText>
</View>
</RootView>
)
}
As I am a novice, I sure I missed some steps, but which one? :)
Thanks for your help!
Top comments (1)
I found the solution and I am going to post my solution.
As I am a beginner, I am not sure if it's the best practice. At least there is a solution.
Résumé of the scenario
Form my setting.tsx, I can select a field to monitor some stations registered for the selected field. If I move to stations.tsx, map.tsx, alarms.tsx, the stations must be filtered with the selected field. If I return to settings.tsx and I select another field, the selected field must taken in consideration in map.tsx, stations.tsx and alarms.tsx. If I close the application, and I later open it, the selected field must be the same. (memory not lost)
Solution
I have created (thank to Khurshid) a hook (I still does know if it should be a hook, a constats or a components)
fieldProvider.tsx
I wrapped my menu between
<FieldProvider>
in app/layout.tsxIn my alarms.tsx, I added
const field = useContext(FieldContext)
and<ThemedText>{field?.idField}</ThemedText>
to print the recorded fieldAt this point, it prints
in setting.tsx I added
const field = useContext(FieldContext)
andfield?.setIdField(value)
. Here is the full codeAs you can see above, I used
field?.idField
to select the value of drop menu.When you change the value of the drop menu, to select another field to be used while filtering the station, the function onFieldChange is called and I save the select field into the context
Now, if I navigate from settings.tsx, stations.tsx or alarms.tsx the field ID is the same as the latest selection of the drop menu.
It works fine for now.
Could you let me know
I hope, it helps. Enjoy!