I've been learning React
and React Native
for less than a week, so there are concepts that I'm still learning.
The code below is a first attempt to code an app with navigation through a bottom tab navigator, more specifically using @react-navigation/material-bottom-tabs
and react-native-paper
. There are 3 screens called Home, Details and Help. Home and Details both contain a button "ask for help", which will both redirect to the Help screen with a special message that is passed as parameter. For example:
<Button
icon="camera"
mode="contained"
onPress={() => navigation.navigate("Help", { txt: "Everything is OK!" })}
style={{ marginTop: 16 }}
>
Ask for help
</Button>
When loading the application, HelpScreen
is initiated with initialParams={{ txt: "nothing" }}
and the screen will display You said nothing
.
From HomeScreen
, clicking on the button redirects me to HelpScreen
with onPress={() => navigation.navigate("Help", { txt: "Everything is OK!" })}
. Therefore the screen will display You said Everything is OK!
.
When moving to another screen and then going back to HelpScreen
with the use of the bottom tabs, I expect HelpScreen
to be re-rendered with its original value. Therefore I expect the screen to say You said nothing
. But no, it still says You said Everything is OK!
. The same behavious happens with DetailsScreen
with another text.
In the HelpScreen
function, I am not saving the parameter to a state. So I don't expect the component to retain the previous value when re-rendering. So why doesn't the component reset to its original value when re-rendering?
Or is it not re-rendering? In which case can you please explain why?
Here is the code:
import { registerRootComponent } from "expo";
import { NavigationContainer } from "@react-navigation/native";
import { NativeStackScreenProps } from "@react-navigation/native-stack";
import { createMaterialBottomTabNavigator } from "@react-navigation/material-bottom-tabs";
import { View } from "react-native";
import { Button, Text } from "react-native-paper";
import { SafeAreaProvider } from "react-native-safe-area-context";
import MaterialCommunityIcons from "react-native-vector-icons/MaterialCommunityIcons";
type NavigationParamList = {
Home: undefined;
Details: undefined;
Help: { txt: string };
};
type PropsHome = NativeStackScreenProps<NavigationParamList, "Home">;
type PropsDetails = NativeStackScreenProps<NavigationParamList, "Details">;
type PropsHelp = NativeStackScreenProps<NavigationParamList, "Help">;
const Tab = createMaterialBottomTabNavigator<NavigationParamList>();
function HomeScreen({ navigation, route }: PropsHome) {
console.log("HOME");
return (
<View style={{ flex: 1, justifyContent: "center", alignItems: "center" }}>
<Text>Route name: {route.name}</Text>
<Button
icon="camera"
mode="contained"
onPress={() =>
navigation.navigate("Help", { txt: "Everything is OK!" })
}
style={{ marginTop: 16 }}
>
Ask for help
</Button>
</View>
);
}
function DetailsScreen({ navigation, route }: PropsDetails) {
console.log("DETAILS");
return (
<View style={{ flex: 1, justifyContent: "center", alignItems: "center" }}>
<Text>Route name: {route.name}</Text>
<Button
icon="camera"
mode="contained"
onPress={() => navigation.navigate("Help", { txt: "HELP ME!" })}
style={{ marginTop: 16 }}
>
Ask for help
</Button>
</View>
);
}
function HelpScreen({ route }: PropsHelp) {
console.log("HELP");
return (
<View style={{ flex: 1, justifyContent: "center", alignItems: "center" }}>
<Text>Route name: {route.name}</Text>
<Text>You said {route.params.txt}</Text>
</View>
);
}
export default function App() {
return (
<SafeAreaProvider>
<NavigationContainer>
<Tab.Navigator initialRouteName="Home" screenOptions={{}}>
<Tab.Screen
name="Home"
options={{
tabBarIcon: ({ color }) => (
<MaterialCommunityIcons name="home" color={color} size={26} />
),
}}
component={HomeScreen}
/>
<Tab.Screen
name="Details"
options={{
tabBarIcon: ({ color }) => (
<MaterialCommunityIcons
name="alien-outline"
color={color}
size={26}
/>
),
}}
component={DetailsScreen}
/>
<Tab.Screen
name="Help"
options={{
tabBarIcon: ({ color }) => (
<MaterialCommunityIcons
name="chat-question-outline"
color={color}
size={26}
/>
),
}}
component={HelpScreen}
initialParams={{ txt: "nothing" }}
/>
</Tab.Navigator>
</NavigationContainer>
</SafeAreaProvider>
);
}
registerRootComponent(App);
Note that I have not used the useState
hook. I understand that this hook would be needed when one wants the retain a value, not reset it.
CodePudding user response:
React Navigation avoids rerendering each screen every time is focused for both better user experience and avoiding unnecessary screen rerendering.
You should override this default behavior and detect when the screen is revisited/focuses and rerender that screen.
It provided utility hooks to listen to the screen is focused - https://reactnavigation.org/docs/function-after-focusing-screen/
Here code refactor with a solution:
import { registerRootComponent } from "expo";
import {useEffect,useState} from "react"
import { NavigationContainer ,useIsFocused} from "@react-navigation/native";
import { NativeStackScreenProps } from "@react-navigation/native-stack";
import { createMaterialBottomTabNavigator } from "@react-navigation/material-bottom-tabs";
import { View } from "react-native";
import { Button, Text } from "react-native-paper";
import { SafeAreaProvider } from "react-native-safe-area-context";
import MaterialCommunityIcons from "react-native-vector-icons/MaterialCommunityIcons";
type NavigationParamList = {
Home: undefined;
Details: undefined;
Help: { txt: string };
};
type PropsHome = NativeStackScreenProps<NavigationParamList, "Home">;
type PropsDetails = NativeStackScreenProps<NavigationParamList, "Details">;
type PropsHelp = NativeStackScreenProps<NavigationParamList, "Help">;
const Tab = createMaterialBottomTabNavigator();
function HomeScreen({ navigation, route }: PropsHome) {
console.log("HOME");
return (
<View style={{ flex: 1, justifyContent: "center", alignItems: "center" }}>
<Text>Route name: {route.name}</Text>
<Button
icon="camera"
mode="contained"
onPress={() =>
navigation.navigate("Help", { txt: "Everything is OK!" })
}
style={{ marginTop: 16 }}
>
Ask for help
</Button>
</View>
);
}
function DetailsScreen({ navigation, route }: PropsDetails) {
console.log("DETAILS");
return (
<View style={{ flex: 1, justifyContent: "center", alignItems: "center" }}>
<Text>Route name: {route.name}</Text>
<Button
icon="camera"
mode="contained"
onPress={() => navigation.navigate("Help", { txt: "HELP ME!" })}
style={{ marginTop: 16 }}
>
Ask for help
</Button>
</View>
);
}
function HelpScreen({ route ,navigation}: PropsHelp) {
const [message,setMessage] = useState("")
const isFocused = useIsFocused()
useEffect(()=>{
setMessage(route.params.txt)
return ()=>{
navigation.setParams({txt:"nothing"})
}
},[isFocused])
return (
<View style={{ flex: 1, justifyContent: "center", alignItems: "center" }}>
<Text>Route name: {route.name}</Text>
<Text>You said {message}</Text>
</View>
);
}
export default function App() {
return (
<SafeAreaProvider>
<NavigationContainer>
<Tab.Navigator initialRouteName="Home" screenOptions={{}}>
<Tab.Screen
name="Home"
options={{
tabBarIcon: ({ color }) => (
<MaterialCommunityIcons name="home" color={color} size={26} />
),
}}
component={HomeScreen}
/>
<Tab.Screen
name="Details"
options={{
tabBarIcon: ({ color }) => (
<MaterialCommunityIcons
name="alien-outline"
color={color}
size={26}
/>
),
}}
component={DetailsScreen}
/>
<Tab.Screen
name="Help"
options={{
tabBarIcon: ({ color }) => (
<MaterialCommunityIcons
name="chat-question-outline"
color={color}
size={26}
/>
),
}}
component={HelpScreen}
initialParams={{ txt: "nothing" }}
/>
</Tab.Navigator>
</NavigationContainer>
</SafeAreaProvider>
);
}
Working Demo - https://snack.expo.dev/@emmbyiringiro/493761