React navigation undefined params - react-native

I'm trying to pass params into a new screen, and implemented it like mentioned here.
I have the following TouchableOpacity button.
<TouchableOpacity
onPress={() => {
this.props.navigation.navigate('SomeScreen', {
title: 'Title',
subTitle: 'Subtitle',
});
}}
>
On the other page (let's call it Somescreen), I have the following:
render() {
const { navigation } = this.props;
const title = navigation.getParam('title');
}
But title above is undefined:
{ params: undefined, routeName: "Somescreen", key: "id-xx" }
My rootStack:
const RootStack = createStackNavigator({
SomescreenA: { screen: SomescreenA },
SomescreenB: { screen: SomescreenB },
}, { headerMode: 'none' });
Why are my params undefined in a new screen?

If you face a situation where your target screen get undefined params, probably you have a nested navigation stack.
Here you have to pass params to the navigate method in this way:
navigation.navigate('Root', {
screen: 'Settings',
params: { user: 'jane' },
});
For more information read this page in the official docs:
https://reactnavigation.org/docs/nesting-navigators/#navigating-to-a-screen-in-a-nested-navigator

In my specific case, I was calling a nested navigator, so I had to manage how send those params to their specific screen, so I did this:
Send params this way...the regular way:
navigation.navigate(
'OrderNavigator',
{itemSelected},
);
Then, from navigator stack I did this:
const OrderNavigator = ({route: {params}}) => {
return (
<Stack.Navigator initialRouteName="Order">
<Stack.Screen name="Order" component={Order} options={{headerShown: false}} initialParams={params} />
</Stack.Navigator>
);
};
And that's it. Then from the screen I got them like this:
const Order = ({route}) => {
const {itemSelected} = route.params;
const {first_name, last_name} = itemSelected;
return (...)
}

I've, unfortunately, encountered cases where navigate(route, params, ...) wouldn't pass the params object, just like you did.
As a workaround, I use the other variant - navigate({routeName, params, action, key}) that you can find here. It always works.

The accepted answer workaround did not work for me, so apparently if you use children to render your component (in screen options) and pass route as a prop, it works

if you are on react navigation v6^ use the useRoute hook to access the params object
const route = useRoute();
useRoute is a hook that gives access to the route object. It's useful when you cannot pass the route prop into the component directly, or don't want to pass it in case of a deeply nested child.
below is an implementation of this
import { useNavigation, useRoute } from '#react-navigation/native';
import { Pressable, Text } from 'react-native';
function Screen1() {
const navigation = useNavigation();
return (
<Pressable
onPress={() => {
navigation.navigate('Screen2', { caption: 'hey' });
}}
>
<Text> Go to Screen 2 </Text>
</Pressable>
);
}
function Screen2() {
const route = useRoute();
return <Text>{route.params.caption}</Text>;
}

Related

Passing navigation to BottomNavigation

In my React Native app, I use react-navigation version 5.
How do I pass the navigation object to the scenes in BottomNavigation?
Here's the component where I create the BottomNavigation:
import React from 'react';
import { BottomNavigation } from 'react-native-paper';
// Components
import CodeScanner from '../../../screens/vendors/CodeScannerScreen';
import Home from '../../../screens/home/HomeScreen';
import Vendors from '../vendors/VendorsStack';
// Routes
const homeRoute = () => <Home />;
const vendorsRoute = () => <Vendors />;
const scanRoute = () => <CodeScanner />;
const HomeTabs = (props) => {
const [index, setIndex] = React.useState(0);
const [routes] = React.useState([
{ key: 'home', title: 'Home', icon: 'newspaper-variant-multiple' },
{ key: 'vendors', title: 'Vendors', icon: 'storefront' },
{ key: 'codescanner', title: 'Scan', icon: 'qrcode-scan' }
]);
const renderScene = BottomNavigation.SceneMap({
home: homeRoute,
vendors: vendorsRoute,
codescanner: scanRoute
});
return (
<BottomNavigation
navigationState={{ index, routes }}
onIndexChange={setIndex}
renderScene={renderScene}
labeled={false} />
);
}
export default HomeTabs;
In this code, I do have the navigation in the props but haven't been able to figure out a way to pass navigation to the screens. Please also note that the vendor one is actually a stack navigator. In particular, that's where I need access to navigation so that I can open up other screens.
You should pass it from tabBar prop like below (I use TypeScript) and BottomTabBarProps:
export declare type BottomTabBarProps = BottomTabBarOptions & {
state: TabNavigationState
descriptors: BottomTabDescriptorMap
navigation: NavigationHelpers<ParamListBase, BottomTabNavigationEventMap>
}
So you pass BottomTabBarProps to your custom tab component
<BottomTab.Navigator
screenOptions={TabBarVisibleOnRootScreenOptions}
initialRouteName={'Explore'}
tabBar={(props: BottomTabBarProps) => <HomeTabs {...props} />}
>
<BottomTab.Screen name="Explore" component={ExploreScreen} />
...
</BottomTab.Navigator>
So inner HomeTabs you got props.navigation
I think you don't need to pass navigation to BottomNavigation. Because in react-navigation v5 navigation can access anywhere with hook
Read this document https://reactnavigation.org/docs/connecting-navigation-prop/
You may pass data to the vendors screen through the route like so:
const HomeTabs = ( props ) => {
...
const [routes] = React.useState([
...
{ key: 'vendors', title: 'Vendors', icon: 'storefront', props: props },
...
]);
...
}
On your vendor screen you can then retrieve and use the data like this:
const VendorsRoute = ({ route }) => {
const props = route.props;
<Vendors />
}

how to set initialRouteName dynamically in the react-native

how to give dynamic initial route name in the react-navigation? if the unit exists we have to redirect to another route or else we have to take user another route.
Note: I'm creating a bottom tab navigator in which I have to set an initial route to that particular bottom tab navigator.
(Not the authentication flow)
import React, {Component} from 'react';
import {createAppContainer} from 'react-navigation';
import {createMaterialBottomTabNavigator} from 'react-navigation-material-bottom-tabs';
... imports
function getInitialScreen() {
AsyncStorage.getItem('unit')
.then(unit => {
return unit ? 'Home' : 'secondTab';
})
.catch(err => {
});
}
const TabNavigator = createMaterialBottomTabNavigator(
{
Home: {
screen: HomeScreen,
navigationOptions: {
.....navigation options
},
},
secondTab: {
screen: secondTab,
},
},
{
initialRouteName: getInitialScreen(),
},
);
export default createAppContainer(TabNavigator);
See according to the docs, initialRoute name should not be a async func .
So ideally what you should do is , anyways you need a splashscreen for your app right, where you display the logo and name of app. Make that page the initialRoute and in its componentDidMount, check for the async function and navigate to ddesired page.
Like what ive done :
createSwitchNavigator(
{
App: TabNavigator,
Auth: AuthStack,
SplashScreen: SplashScreen,
},
{
initialRouteName: 'SplashScreen',
},
),
And inside SplashScreen im doing :
componentDidMount(){
if (token) {
this.props.navigation.navigate('App');
} else {
this.props.navigation.navigate('Auth');
}
}
Hope its clear. Feel free for doubts
As you can see here:
If you need to set the initialRouteName as a prop it is because there
is some data that you need to fetch asynchronously before you render
the app navigation. another way to handle this is to use a
switchnavigator and have a screen that you show when you are fetching
the async data, then navigate to the appropriate initial route with
params when necessary. see docs for a full example of this.
Take a look at here.
You'll find more description!
Also quick fix for this situation is check your condition inside SplashScreen componentDidMount() function
Example of SplashScreen :
componentDidMount(){
AsyncStorage.getItem('unit')
.then(unit => {
if(unit){
this.props.navigation.navigate('Home')
}else{
this.props.navigation.navigate('secondTab')
}
})
.catch(err => {
});
}
You can check the condition on the main page or App.js
render() {
const status = get AsyncStorage.getItem('unit');
if(status != null)
{ return <Home/> }
else
{ return <AnotherScreen/> }
}
But we can switch between pages if we use stacknavigator or switchnavigator..
We cant goto the particular tabs directly according to my knowledge. (Correct me if I am wrong).
The official documentation gives you this example to achieve pretty much what you want:
isSignedIn ? (
<>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Profile" component={ProfileScreen} />
<Stack.Screen name="Settings" component={SettingsScreen} />
</>
) : (
<>
<Stack.Screen name="SignIn" component={SignInScreen} />
<Stack.Screen name="SignUp" component={SignUpScreen} />
</>
)
Ok So in your navigation page create state and change its value accordingly AsyncStorage like
render() {
const status = get AsyncStorage.getItem('unit');
if(status != null)
{ setState({ name : 'Home'}) }
else
{ setState({ name : 'anotherTab'}) }
}
then pass that state to tabNavigation
initialRouteName: this.state.name,
and this state , setState functions are classed based so you have to use useState instead to initial state on your page

Send props on navigation goBack

Basically I have three screens, the first is a stack navigator:
const stackNav = createStackNavigator({
Main: {
screen: MainScreen,
navigationOptions:({navigation}) => ({
header: null,
})
},
Detail: {
screen: DetailScreen,
navigationOptions: (props) => ({
title: "Detail",
})
}
})
The second one I have a button to go to the Detail screen:
<TouchableOpacity onPress={() => this.props.navigation.navigate("Detail", {name: l.name, subtitle: l.subtitle})}>
The last one is just information, I would like to click a button and execute:
this.props.navigation.goBack(), but sending props to the second screen (MainScreen), a setState and a integer id, how can I do that?
--EDIT WITH PARAMS--
I have this function in MainScreen:
handleOrdem(texto) {
console.log('texto: '+texto)
this.setState({
param: global.ordemAtiva,
ordemAtiva: !this.state.ordemAtiva
});
}
//The onPress code:
onPress={() => this.props.navigation.navigate("Detail", {name: l.name, subtitle: l.subtitle, ordemFunc: () => this.handleOrdem()})}>
and this is how I call it in Detail.screen:
execBack(param){
console.log('param: '+param);
this.props.navigation.state.params.ordemFunc(param);
this.props.navigation.goBack();
}
//Button to do it
onPress={() => this.execBack('test')}
Create a Method in parent screen
returnData(){
PERDROM_EVENT_WITH_RECEIVED_DATA
}
Then bind this method returnData with navigation code while executing navigation code
this.props.navigation.navigate("Detail", {name: l.name, subtitle: l.subtitle , returnData: this.returnData.bind(this)})}
In child Component call returnData method before call of goBack()
this.props.navigation.state.params.returnData(RETURN_DATA_YOU_WANT);
this.props.navigation.goBack();
Handling return data
Suppose you want two parameters back then add two parms in returnData() method
For example we took first param is boolean and second param String
returnData(flag,id){
USE THIS `flag` and `id` to update state or method call or
What ever you wanted too.
}
And inside Child component pass these two param
this.props.navigation.state.params.returnData(VALUE_OF `flag`, Value of `id`);
FOR EDIT WITH PARAMS
replace your code of navigation with this line
this.props.navigation.navigate("Detail", {name: l.name, subtitle: l.subtitle, ordemFunc: this.handleOrdem.bind(this)})>
You have to bind method not to call with arrow function
So the problem is
ordemFunc: () => this.handleOrdem()
Replace this line with
ordemFunc: this.handleOrdem.bind(this)
I came across this exact same issue and the problem is actually quite simple. We will utilise a callback for passing the params when we trigger goBack()
For example lets say I have two Views: ViewA and ViewB.
For which I will do as follows:
import useNavigation hook for setting up navigation.
When navigating to the next screen pass a callback ie: function with the specified paramaters for the values you would like to pass back to ViewA.
In ViewB use the route prop to get access to the params. In here you will find your callback.
Use your callback and pass in the correct arguments to your callback.
Call the navigation.goBack() to return to ViewA
In ViewA you will now have access to your value in your callback.
import React from "react";
import { View, Button } from "react-native";
import { useNavigation } from "#react-navigation/native";
const ViewA = () => {
const navigation = useNavigation();
return (
<View>
<Button
onPress={() =>
navigation.navigate("ViewB", {
handleItem: (item) => console.log(item), // will log out "Your Item"
})
}
/>
</View>
);
};
const ViewB = ({ route }) => {
const navigation = useNavigation;
return (
<View>
<Button
onPress={() => {
route.params.handleItem("Your Item");
navigation.goBack();
}}
/>
</View>
);
};
If you are using react-navigation v2 you no need to use navigation.goBack() to go back to Main screen
this.props.navigation.navigate('Main', { myParam: value }) will declaratively handle the navigation back (with same transition) for you
Maybe you can save the data in the global state and call it again when you goBack
React.useEffect(() => {
const unsubscribe = navigation.addListener('focus', () => {
console.log("its focus");
//call the new data and update state here
});
return unsubscribe;
}, [navigation]);

How to navigate between different nested stacks in react navigation

The Goal
Using react navigation, navigate from a screen in a navigator to a screen in a different navigator.
More Detail
If I have the following Navigator structure:
Parent Navigator
Nested Navigator 1
screen A
screen B
Nested Navigator 2
screen C
screen D
how do I go from screen D under nested navigator 2, to screen A under nested navigator 1? Right now if I try to navigation.navigate to screen A from screen D there will be an error that says it doesn't know about a screen A, only screen C and D.
I know this has been asked in various forms in various places on this site as well as GitHub(https://github.com/react-navigation/react-navigation/issues/983, https://github.com/react-navigation/react-navigation/issues/335#issuecomment-280686611) but for something so basic, there is a lack of clear answers and scrolling through hundreds of GitHub comments searching for a solution isn't great.
Maybe this question can codify how to do this for everyone who's hitting this very common problem.
In React Navigation 5, this becomes much easier by passing in the screen as the second parameter:
navigation.navigate('Nested Navigator 2', { screen: 'screen D' });
You can also include additional levels if you have deeply nested screens:
navigation.navigate('Nested Navigator 2', {
screen: 'Nested Navigator 3', params: {
screen: 'screen E'
}
});
Update: For React Navigation v5, see #mahi-man's answer.
You can use the third parameter of navigate to specify sub actions.
For example, if you want to go from screen D under nested navigator 2, to screen A under nested navigator 1:
this.props.navigation.navigate(
'NestedNavigator1',
{},
NavigationActions.navigate({
routeName: 'screenB'
})
)
Check also:
https://reactnavigation.org/docs/nesting-navigators/
React Navigation v3:
Navigation.navigate now takes one object as the parameter. You set the stack name then navigate to the route within that stack as follows...
navigation.navigate(NavigationActions.navigate({
routeName: 'YOUR_STACK',
action: NavigationActions.navigate({ routeName: 'YOUR_STACK-subRoute' })
}))
Where 'YOUR_STACK' is whatever your stack is called when you create it...
YOUR_STACK: createStackNavigator({ subRoute: ... })
On React Navigation v5 you have here all the explanation:
https://reactnavigation.org/docs/nesting-navigators#navigating-to-a-screen-in-a-nested-navigator
Route definition
function Root() {
return (
<Stack.Navigator>
<Stack.Screen name="Profile" component={Profile} />
<Stack.Screen name="Settings" component={Settings} />
</Stack.Navigator>
);
}
function App() {
return (
<NavigationContainer>
<Drawer.Navigator>
<Drawer.Screen name="Home" component={Home} />
<Drawer.Screen name="Root" component={Root} />
</Drawer.Navigator>
</NavigationContainer>
);
}
Instruction
navigation.navigate('Root', { screen: 'Settings' });
In React Navigation v5, you can do something like:
navigation.navigate('Root', {
screen: 'Settings',
params: {
screen: 'Sound',
params: {
screen: 'Media',
},
},
});
In the above case, you're navigating to the Media screen, which is in a navigator nested inside the Sound screen, which is in a navigator nested inside the Settings screen.
In React Navigation v5/v6
Navigation to Specific Screen on a Stack
navigation.navigate('Home', {
screen: 'Profile',
params: {userID: 1}
}
)
What If We Nest More?
Consider this structure:
NAVIGATOR:
*StackA
*ScreenC
*ScreenD
*StackB
*ScreenI
*StackE
*ScreenF
*StackG
*ScreenJ
*ScreenH
We want to get from ScreenC inside StackA all the way to ScreenH in StackB.
We can actually chain the parameters together to access specific screens.
navigation.navigate('StackB',{
screen: 'StackE',
params: {
screen: 'StackG',
params: {
screen: 'ScreenH'
}
}
}
)
For more information
In React Navigation 3
#ZenVentzi, Here is the answer for multi-level nested navigators when Nested Navigator 1 has Nested Navigator 1.1.
Parent Navigator
Nested Navigator 1
Nested Navigator 1.1
screen A
screen B
Nested Navigator 2
screen C
screen D
We can just inject NavigationActions.navigate() several times as needed.
const subNavigateAction = NavigationActions.navigate({
routeName: 'NestedNavigator1.1',
action: NavigationActions.navigate({
routeName: 'ScreenB',
params: {},
}),
});
const navigateAction = NavigationActions.navigate({
routeName: 'NestedNavigator1',
action: subNavigateAction,
});
this.props.navigation.dispatch(navigateAction);
UPDATE
For React Navigation 5, please check #mahi-man's answer above.
https://stackoverflow.com/a/60556168/10898950
React Navigation v6
docs
function Home() {
return (
<Tab.Navigator>
<Tab.Screen name="Feed" component={Feed} />
<Tab.Screen name="Messages" component={Messages} />
</Tab.Navigator>
);
}
function App() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen
name="Home"
component={Home}
options={{ headerShown: false }}
/>
<Stack.Screen name="Profile" component={Profile} />
<Stack.Screen name="Settings" component={Settings} />
</Stack.Navigator>
</NavigationContainer>
);
}
navigation.navigate('Home', { screen: 'Messages' });
try this,
Parent Navigator
Nested Navigator 1
screen A
screen B
Nested Navigator 2
screen A
screen C
screen D
and then, there is no need to go A in 1 from D in 2, you can just go A from D both in 2, you can check here image or A stack navigator for each tab
Complete freedom: singleton w/ navigationOptions
If you have a situation where you have multiple navigation stacks and sub stacks, this can be frustrating to know how to get a reference to the desired stack given how React Navigation is setup. If you were simply able to reference any particular stack at any given time, this would be much easier. Here's how.
Create a singleton that is specific to the stack you want to reference anywhere.
// drawerNavigator.js . (or stackWhatever.js)
const nav = {}
export default {
setRef: ref => nav.ref = ref,
getRef: () => nav.ref
}
Set the reference on desired navigator using navigatorOptions
import { createBottomTabNavigator } from 'react-navigation'
import drawerNavigation from '../drawerNavigator'
const TabNavigation = createBottomTabNavigator(
{
// screens listed here
},
{
navigationOptions: ({ navigation }) => {
// !!! secret sauce set a reference to parent
drawerNavigation.setRef(navigation)
return {
// put navigation options
}
}
}
)
Now you can reference drawerNavigator anywhere inside or outside
// screen.js
import drawerNavigator from '../drawerNavigator'
export default class Screen extends React.Component {
render() {
return (
<View>
<TouchableHighlight onPress={() => drawerNavigator.getRef().openDrawer()}>
<Text>Open Drawer</Text>
</TouchableHighlight>
</View>
)
}
}
Explanation
Within Step 2, a Tab Navigator is one of the screens within a Drawer Navigator. Tab Navigator needs to close the drawer but also anywhere within your app, you can call drawerNavigator.getRef().closeDrawer() after this step is performed. You are not limited to having direct access to props.navigation after that step.
If nothing else works (as in my case), just do:
Main/Root/App.js:
<StackNavigator ref={(x) => (global.stackNavigator = x)} />
Anywhere:
global.stackNavigator.dispatch(
NavigationActions.navigate({
routeName: 'Player',
params: { },
}),
);
In React Navigation V5, you can do like this:
but remember to you placing this on the parent side
this.props.navigation.navigate(
'Nested Navigator 1',
{name: 'jane'},
this.props.navigation.navigate('Screen A', {id: 2219}),
);
While working on a react-native project, i came across same situation. I have tried multiple ways in navigating to screen but failed.
After many trials, I tried passing parents navigation object to children and made a navigation function call and it worked.
Now coming to your issues, If you want to navigation from screen D to screen A do follow these steps.
-> Pass nested navigator 2 navigation props to its children using screenProps.
export default class Home extends Component {
static navigationOptions = {
header:null
};
constructor(props) {
super(props);
this.state = {
profileData: this.props.navigation.state.params,
route_index: '',
}
}
render() {
return (
<ParentNavigator screenProps={this.props.navigation} />
);
}
}
export const ParentNavigator = StackNavigator({
// ScreenName : { screen : importedClassname }
Nested1: { screen: nested1 },
Nested2: { screen : nestes1 }
});
export const nested1 = StackNavigator({
ScreenA: { screen: screenA },
ScreenB: { screen : screenB }
});
export const nested2 = StackNavigator({
ScreenC: { screen: screenC },
ScreenD: { screen : screenD }
});
You can receive the navigation in children using
const {navigate} = this.props.screenProps.navigation;
Now this navigate() can be used to navigate between children.
I accept that this process is little confusing but i couldn't find any solutions so had to go with this as i have to complete my requirement.
I've found also such solution here:
onPress={() =>
Promise.all([
navigation.dispatch(
NavigationActions.reset({
index: 0,
// TabNav is a TabNavigator nested in a StackNavigator
actions: [NavigationActions.navigate({ routeName: 'TabNav' })]
})
)
]).then(() => navigation.navigate('specificScreen'))
}
const subAction = NavigationActions.navigate({ routeName: 'SignInScreen' });
AsyncStorage.clear().then(() =>
this.props.navigation.navigate('LoggedOut', {}, subAction));
LoggedOut is the stack name where signIn screen is placed.
My goal was to have the authentication screens all share the same background and the rest of the app using the regular stack transition.
After hours I've found the solution is to have the createStackNavigator() in the same file as your component wrapper. So that you can successfully expose the static router as the document stated. This will avoid the You should only render one navigator explicitly in your app warning and you can use this.props.navigation.navigate('AnyScreen') to navigate to any nested screen.
AuthRouter.js
export const AuthNavigationStack = createStackNavigator(
{
Login: {
screen: Login
},
CreateAccount: {
screen: CreateAccount
}
}
);
export default class AuthContainer extends React.Component {
constructor( props ) {
super( props );
}
static router = AuthNavigationStack.router;
render() {
return (
<ImageBackground
style={ {
width: '100%',
height: '100%'
} }
source={ require( '../Images/johannes-andersson-yosimite.jpg' ) }
blurRadius={ 10 }
>
<StatusBar
barStyle="dark-content"
backgroundColor="transparent"
translucent={ true }
/>
<AuthNavigationStack navigation={ this.props.navigation } />
</ImageBackground>
);
}
}
MessengerRouter.js
export const MessengerStackNavigator = createStackNavigator(
{
Chat: {
screen: Chat,
},
User: {
screen: User,
},
}
);
export default class MainContainer extends React.Component {
constructor( props ) {
super( props );
}
static router = MessengerStackNavigator.router;
render() {
return <MessengerStackNavigator navigation={ this.props.navigation } />;
}
}
Router.js
import { createStackNavigator } from 'react-navigation';
import AuthRouter from './AuthRouter';
import MessengerRouter from './MessengerRouter';
export const RootNavigationStack = createStackNavigator( {
AuthContainer: {
screen: AuthRouter,
navigationOptions: () => ( {
header: null
} )
},
MessengerRouter: {
screen: MessengerRouter,
navigationOptions: () => ( {
header: null
} )
}
} );
RootContainer.js
import { RootNavigationStack } from '../Config/Router';
class RootContainer extends Component {
render() {
return <RootNavigationStack />;
}
}
Notes:
Pass header: null from the RootNaviagtionStack to the nested stacks to remove the overlapping header
If you navigate from Nested A to Nested B and use the back button, it will return you to the first screen in Nested B. Not a big problem but I haven't figured out how to fix it.
This is another way to navigate to nested screen using Version: 5.x. It worked without any additional configuration. More info here: https://reactnavigation.org/docs/use-link-to
const linkTo = useLinkTo();
// ...
// Just call this
linkTo(`/main/sub/subB`);

Where to initialize data loading with react-navigation

I'm using react-navigation and here is my structure :
The root stack navigator :
export const Root = StackNavigator({
Index: {
screen: Index,
navigationOptions: ({ navigation }) => ({
}),
},
Cart: {
screen: Cart,
navigationOptions: ({ navigation }) => ({
title: 'Votre panier',
drawerLabel: 'Cart',
drawerIcon: ({ tintColor }) => <Icon theme={{ iconFamily: 'FontAwesome' }} size={26} name="shopping-basket" color={tintColor} />
}),
},
...
My structure looks like this :
StackNavigator (Root)
DrawerNavigator (Index)
TabNavigator
MyPage
MyPage (same page formatted with different datas)
...
So my question is, where do I load my data, initialize my application ? I need somewhere called once, called before the others pages.
The first page displayed in my application is the MyPage page. But as you can see, because of the TabNavigator, if I put my functions inside, it will be called many times.
Some will says in the splashscreen, but I'm using the main splashscreen component and I don't have many controls over it.
I thought about my App.js where we create the provider, but I don't think this is a good idea ?
const MyApp = () => {
//TODO We're loading the data here, I don't know if it's the good decision
ApplicationManager.loadData(store);
SplashScreen.hide();
return (
<Provider store={store}>
<Root/>
</Provider>
);
};
What is the good way to do it ?
class MyApp extends Component {
state = {
initialized: false
}
componentWillMount() {
// if this is a promise, otherwise pass a callback to call when it's done
ApplicationManager.loadData(store).then(() => {
this.setState({ initialized: true })
})
}
render() {
const { initialized } = this.state
if (!initialized) {
return <SplashScreen />
}
return (
<Provider store={store} >
<Root />
</Provider>
);
}
}
TabNavigator by default renders/loads all its child components at the same time, but if you set property lazy: true components will render only if you navigate. Which means your functions will not be called many times.
const Tabs = TabNavigator(
{
MyPage : {
screen: MyPage
},
MyPage2 : {
screen: MyPage,
}
}
},
{
lazy: true
}
);
If you use this structure and call fetching data inside of MyPage you can add logic in componentWillReceiveProps that will check is data already in store and/or is it changed before fetching new data. Calling your fetch functions from MyPage gives you the ability to pull fresh data on every page/screen visit or do "pull to refresh" if you need one.
You could also pull initial data in splashscreen time, I would just not recommend pulling all your app data, data for all screens, at that time since you probably don't need it all at once. You can do something like:
class MyApp extends Component {
state = {
initialized: false
}
componentWillMount() {
// if this is a promise, otherwise pass a callback to call when it's done
ApplicationManager.loadData(store).then(() => {
this.setState({ initialized: true })
})
}
render() {
const { initialized } = this.state
if (!initialized) {
return null
}
return (
<Provider store={store} >
<Root />
</Provider>
);
}
}
class Root extends Component {
componentDidMount() {
SplashScreen.hide();
}
...
}
You should do it in App.js or where you initialize your StackNavigator. If I were you, I would put a loading screen, which would get replaced by the StackNavigator structure once the data is ready.
I wouldn't do it in the App because you lose control. Sadly I haven't used react-navigation or redux but I see that the TabNavigator has a tabBarOnPress method, which I would use to trigger the loading. You can load every page data on demand.
https://reactnavigation.org/docs/navigators/tab#tabBarOnPress