Pass state data between component through navigator - reactjs

my issue is look so simple but I can't figure is out how to solve it
I have two component that I navigate to them by using StackNavigator,
in one of the component I have state with values, I need to pass the state values to the other component, but as I said I using StackNavigator to go from one component to the other, I trying to figure out how can I do so
my navigator:
const Navigate = StackNavigator(
{
Home: { screen: HomeScreen },
Riddles: { screen: RiddlesScreen },
Introduction: { screen: IntroductionScreen },
About: { screen: AboutScreen },
},
{ headerMode: 'screen' }
);
My homescreen class:
class HomeScreen extends Component {
static navigationOptions = {
header: null
}
render() {
return (
<View>
<Home navigation={this.props.navigation} />
</View>
);
}
}
my riddlescreen class:
class RiddlesScreen extends Component {
static navigationOptions = {
header: null
}
render() {
return (
<View>
<Riddles navigation={this.props.navigation} />
</View>
);
}
}
in the Riddles component (at the RiddleScreen class) I have state with the values that I need to pass to Home component (at HomeScreen class).
what is the best way to achieve this goal?
every help really appreciated! thanks.

You can pass data between screens while navigating. More info here.
Example
<Button onPress={() => {this.props.navigation.navigate('SomeScreen', { some: this.state.someValue})}} />
// and in SomeScreen
console.log(this.props.navigation.state.params.some);

Related

React Native - pass props from One screen to another screen (using tab navigator to navigate)

I need to pass data from my HomeScreen to my SecondScreen. There are a ton of examples of how to do this if i'm clicking a button on the HomeScreen to navigate to SecondScreen, but can't find anything showing how to pass to SecondScreen if I'm using a v2 bottom tab navigator to go from HomeScreen to SecondScreen. I've tried screenprops and a couple other methods and spent about 8 hours trying to figure it out but could not get it to work. Any idea how to do this? Please, any hint would be amazing. Here is my code:
MainTabNavigator.js:
const config = Platform.select({
web: { headerMode: 'screen' },
default: {},
});
const HomeStack = createStackNavigator(
{
Home: HomeScreen,
},
config
);
HomeStack.navigationOptions = {
tabBarLabel: 'Home',
tabBarIcon: ({ focused }) => (
<MaterialIcons name="home" size={32} />
),
};
HomeStack.path = '';
const SecondStack= createStackNavigator(
{
Second: SecondScreen,
},
config
);
SecondStack.navigationOptions = {
tabBarLabel: 'Second screen stuff',
tabBarIcon: ({ focused }) => (
<MaterialIcons name="SecondScreenIcon" size={32} />
),
};
SecondStack.path = '';
const tabNavigator = createBottomTabNavigator({
HomeStack,
SecondScreen
});
tabNavigator.path = '';
export default tabNavigator;
HomeScreen.js:
class HomeScreen extends Component {
constructor(props){
super(props);
}
componentDidMount(){
this.setState({DataFromHomeScreen: 'my data that Im trying to send to SecondScreen'})
}
//....
SecondScreen.js:
class SecondScreen extends Component {
constructor(props){
super(props);
}
render()
return(
<View>{this.props.DataFromHomeScreen}</View>
)
//....
****Please find THINGS I'VE TRIED below:****
HomeScreen.js: when i do this it receives it at first but then passes null
render(){
return(
<View>
//all of my home screen jsx
<SecondScreen screenProps={{DataFromHomeScreen : 'data im trying to pass'}}/>
</View>
)
}
MaintTabNavigator.js: when i do this it receives it at first but then passes null
HomeStack.navigationOptions = {
tabBarLabel: 'Home',
tabBarIcon: ({ focused }) => (
<MaterialIcons name="home" size={32} />
),
};
<HomeStack screenProps={{DataFromHomeScreen:'data im trying to pass'}}/>
HomeStack.path = '';
I've tried like 5 other ways too that I can't even remember at this point. I don't want to have to call my database again in the second screen to get user info. Nobody I know knows react or react native. The React Native documentation at https://reactnavigation.org/docs/en/stack-navigator.html is minimal at best, only showing the below:
const SomeStack = createStackNavigator({
// config
});
<SomeStack
screenProps={/* this prop will get passed to the screen components as this.props.screenProps */}
/>
even if you go to the examples in the documentation and search for the word 'screenprop' you will not see any mention of the screen prop feature in either of the examples. All questions that I've seen only address how to pass props on button click which is easy. Is what I'm trying to do possible? I'm sure I'm not the only person using tab navigator who's retrieved data in the homescreen and need to pass it to other screens . Any advice helps. thanks.
ps.
Here is my Sign in class that is calling the Home screen:
class SignInScreen extends React.Component {
static navigationOptions = {
title: 'Please sign in',
};
render() {
return (
<View
style={styles.container}
contentContainerStyle={styles.contentContainer}>
<View>
<SocialIcon
title='Continue With Facebook'
button
type='facebook'
iconSize="36"
onPress={this._signInAsync}
/>
</View>
);
}
_signInAsync = async () => {
let redirectUrl = AuthSession.getRedirectUrl();
let result = await AuthSession.startAsync({
authUrl:
`https://www.facebook.com/v2.8/dialog/oauth?response_type=token` +
`&client_id=${FB_APP_ID}` +
`&redirect_uri=${encodeURIComponent(redirectUrl)}`,
});
var token = result.params.access_token
await AsyncStorage.setItem('userToken', token);
await fetch(`https://graph.facebook.com/me?fields=email,name&access_token=${token}`).then((response) => response.json()).then((json) => {
this.props.navigation.navigate('Home',
{
UserName : json.name,
FBID : json.id,
email : json.email
});
}) .catch(() => {
console.log('ERROR GETTING DATA FROM FACEBOOK')
});
};
}
export default SignInScreen;
I think you're calling your database in componentDidMount in your HomeScreen component, (I'm right?) and because another component in the same hierarchy needs the same data, you should considerer wrapping this into a new component and do the call to your data in that father component, then you pass the data to all the children that needs it. This is the react way to do things. The state of HomeScreen should not have the data, your data should live in a parent component in a higher hierarchy and pass the data to children as props.
In this way when you create your tabs you can pass the props as the react native docs suggest:
import { createBottomTabNavigator, BottomTabBar } from 'react-navigation-tabs';
const TabBarComponent = (props) => (<BottomTabBar {...props} />);
const TabScreens = createBottomTabNavigator(
{
tabBarComponent: props =>
<TabBarComponent
{...props}
style={{ borderTopColor: '#605F60' }}
/>,
},
);
Another solution could be to use a global state management with Redux or something similar.
I hope this helps.
Edit:
class Home extends React.Component{
constructor(props){
super(props);
this.state = {data: null}
}
componentDidMount() {
//get your props from navigation (your facebook credentials)
//your call to database
this.setState({data: yourResponseData});
}
render(){
const TabNavigator = createBottomTabNavigator(
{
HomeScreen: props =>
<HomeScreenStack
{...this.state.data}
/>,
SecondStack: props =>
<SecondStack
{...this.state.data}
/>,
},
);
return(
<TabNavigator />
)
}
}
const App = createAppContainer(Home);
export default App;
Use this.props.navigation.navigate.
In your HomeScreen, once you have the data you want to send, then navigate over to SecondScreen like so:
this.props.navigation.navigate('Second', { data: yourData })
To access this in SecondScreen whenever it is navigated to using navigation props, you can use NavigationEvents along with this.props.navigation.getParam.
/* your imports */
import { NavigationEvents } from 'react-navigation';
export default class SecondScreen extends React.Component {
/* your methods and properties */
render() {
<View>
<NavigationEvents
onDidFocus={() => this.setState({ data: this.props.navigation.getParam('data', {}) })}
/>
{ /* your SecondScreen render code */ }
</View>
}
}
Edit: For example, with your SignInScreen implementation, to access the props, use:
const username = this.props.navigation.getParam('UserName', '')
const fbid = this.props.navigation.getParam('FBID', 0)
const email = this.props.navigation.getParam('email', '')
This is the basic approach that I am using:
import {createBottomTabNavigator} from '#react-navigation/bottom-tabs';
const TestComponent = (props) => {
return <Text>{`TestComponent: ${props.name}`}</Text>;
};
const Home = () => {
const Tab = createBottomTabNavigator();
return (
<View style={{flex: 1}}>
<Tab.Navigator>
<Tab.Screen name="Screen 1">
{() => <TestComponent name="test 1" />}
</Tab.Screen>
<Tab.Screen name="Screen 2">
{() => <TestComponent name="test 2" />}
</Tab.Screen>
</Tab.Navigator>
</View>
);
};
Notice that to pass the props to a Screen I am using a child function instead of passing a value to component. The child function can then return the component you want in the syntax you are used to, that has the props available. In this case, the props are the simple name, but you can expand this to handle your state.
I ended up using Redux, it only took me like 100 read throughs and attempts to learn it, but once I learned it it's amazing and simple.

undefined is not an object(evaluating 'this.props.navigation.navigate')

Can someone help me? I keep getting the error (See title).
I know there are a lot of people asking this question before in StackOverflow. I read their answers but when I paste the solution, I still keep hiting this error.
Below are the code.
Thank you very much.
//Jimmy code
import { Navigation } from 'react-native-navigation';
import { StackNavigator } from 'react-navigation';
import { Merchant2 } from "./../Merchant2/Merchant2";
export class Login extends React.Component{
constructor(prop) {
super(prop);
Navigation.events().bindComponent(this);
this.state = {
userName: "",
password: "",
isVerify: false
};
this.onPress=this.onPress.bind(this);
}
onPress = () => {
//const { navigate } = this.props.navigation;
navigate('Page2');
alert(1)
}
render() {
navigationOptions = {
title: 'Results',
};
return (
<View style={[styles.formFooter]}>
<TouchableHighlight style={[commonStyles.alignItemsCenter, styles.loginBtn]}
activeOpacity={1}
underlayColor={'#cdcdcf'}
onPress={this.onPress.bind(this)}
>
);
};
}
const App = StackNavigator({
Home: { screen: Merchant2 },
});
export default App;
A few things here:
You should use methods with names different than the native ones such as 'onPress'
If you don't have a navigation props, it's simply because you never references it in your stack navigation. All items who's view is specified in a navigation object should have a navigation props.
navigationOptions should be tagged static if you define it in your component or like Login.navigationOptions. Also, if you want to add navigation options from the action of elements you create in your navigation options, you can do this like (in your component) : static navigationOptions = ({navigation}) => ({your navigation options here})

TypeError: No "routes" found in navigation state

I am using createMaterialTopTabNavigator from react-navigation in which i have two separate screens UpdatesStack and ShopsStack and i want to navigate to other screen from these screens so i written like <Toptab navigation={this.props.navigation} /> and it showing me following red screen error.
And if i write like <Toptab /> then there is no error but i am not able to navigate.
so how can i solve this problem and able to navigate.
code
class Parenthome extends Component {
render() {
const { navigate } = this.props.navigation;
return (
<View style={styles.container}>
<ToolbarAndroid
style={styles.toolbar}
title="Toolbar"
titleColor="#ff6600"
/>
<Toptab navigation={this.props.navigation} />
</View>
);
}
}
const UpdatesStack = createStackNavigator(
{
Updates: { screen: Home }
},
{
initialRouteName: "Updates"
}
);
const ShopsStack = createStackNavigator(
{
Shops: { screen: Conshop }
},
{
initialRouteName: "Shops"
}
);
const Toptab = createMaterialTopTabNavigator({
Updatestab: { screen: UpdatesStack },
Shopstab: { screen: ShopsStack }
});
export default Parenthome;
I know it's late but just to answer for those who stumble on this from Search Engines:
Why don't you export default TopTab itself. There seems no need to wrap TopTab with ParentTheme component in your use case. You can style the TopTab navigator itself and render it like any other component.
If you must wrap the TopTab you need to have the router from the TopTab accessible, in addition to the navigation prop. This way they both refer to the same router. Simply put, add in ParentTheme:
static router = TopTab.router;
Check out Custom Navigators for more info. https://reactnavigation.org/docs/en/custom-navigators.html
if you are using functional react components with hooks you won't be able to declare a static variable inside your components because they are not JS classes.
Instead declare the router variable as follows:
const reactComponent = (props) => {
/* your component logic and render here */
}
reactComponent.router = TopTab.router; //equivalent to static variable inside a class
export default reactComponent

Integrate StackNavigator with TabNavigator

How do I combine StackNavigator and TabNavigator?
My following code works:
index.android.js :
import React, { Component } from 'react';
import { AppRegistry, Text, View } from 'react-native';
import { StackNavigator,TabNavigator } from 'react-navigation';
import TestComp1 from './src/components/TestComp1'
import TestComp2 from './src/components/TestComp2'
import TestComp3 from './src/components/TestComp3'
import TestComp4 from './src/components/TestComp4'
import TestComp5 from './src/components/TestComp5'
export default class myApp extends Component {
render() {
return (
<MyApp />
);
}
}
const MyApp = StackNavigator({
TestComp1: {screen:TestComp1},
TestComp2: {screen:TestComp2}
});
const Tabs = TabNavigator({
TestComp3: {screen:TestComp3},
TestComp4: {screen:TestComp4}
TestComp5: {screen:TestComp5}
});
AppRegistry.registerComponent('myApp', () => myApp);
This works only for StackNavigator. I want to keep the existing navigation and integrate TabNavigation. Now in TestComp1 if I do the following:
TestComp1 :
import { StackNavigator, TabNavigator } from 'react-navigation';
import { FooterTabs } from './routes/FooterTabs';
export default class HomePage extends Component {
static navigationOptions = {
header: null
};
render() {
return(
<View style={styles.container}>
<View style={styles.mainContent}>
<Button
onPress={() => this.props.navigation.navigate('TestComp1', {name: 'Lucy'})}
title="NewPage"
/>
<FooterTabs /> //Page shows all StackNavigator screens if I add this
</View>
</View>
)
}
}
And the FooterTabs:
import { StackNavigator, TabNavigator } from 'react-navigation';
import TestComp3 from '../TestComp3';
import TestComp4 from '../TestComp4';
import TestComp5 from '../TestComp5';
export const FooterTabs = TabNavigator({
Tab1: {
screen: TestComp3
},
Tab2: {
screen: TestComp4
},
Tab3: {
screen: TestComp5
},
})
The FooterTabs is shown but TestComp1 and TestComp2 are also shown everything below one another. How do I fix this? Thanks.
UPDATE 1:
UPDATE 2:
const Tabs = TabNavigator({
TestComp3: {screen:TestComp1},
TestComp4: {
screen:TestComp4,
navigationOptions: ({ navigation }) => ({
title: "TestComp4",
tabBarIcon: ({ tintColor }) => <MaterialIcons name="accessibility" size={20}/>
})
}
UPDATE 3
I added another const for DrawerNavigator and configured it like this:
const Drawer = DrawerNavigator({
First:{
screen: DrawerScreen1
},
Second:{
screen: DrawerScreen2
}
},{
initialRouteName:'First',
drawerPosition: 'left'
});
And included in the app:
const MyApp = StackNavigator({
TestComp1: {screen:TestComp1},
TestComp2: {screen:TestComp2},
Tabs: {
screen: Tabs
},
Drawer: {
screen: Drawer
},
}, {
initialRouteName: "Tabs"
});
I'm calling it :
<Button
onPress={() => this.props.navigation.navigate('DrawerOpen')}
title="Show Drawer"
/>
OnPress of this button the DrawerScreen1 is called but as a component, it does not show as a drawer from the left. What am I missing?
You can try this:
const Tabs = TabNavigator({
TestComp3: {screen:TestComp3},
TestComp4: {screen:TestComp4}
TestComp5: {screen:TestComp5}
});
const MyApp = StackNavigator({
TestComp1: {screen:TestComp1},
TestComp2: {screen:TestComp2},
Tabs: {
screen: Tabs
}
}, {
initialRouteName: "Tabs"
});
and remove <FooterTabs /> from TestComp1
Let's see now. What you need is first a StackNavigator then inside one of the screens of the StackNavigator you need a TabNavigator. Right?
The answer to this would be the following:
For your index.android.js:
import React, { Component } from 'react';
import { AppRegistry, Text, View } from 'react-native';
import { StackNavigator } from 'react-navigation';
import TestComp1 from './src/components/TestComp1'
import TestComp2 from './src/components/TestComp2'
// Don't need to export default here since this is the root component
// that is registered with AppRegistry and we don't need to import it anywhere.
class myApp extends Component {
render() {
return (
<MyApp />
);
}
}
// Notice that these two screens will consist the navigation stack.
// Assume, TestComp1 contains our Tabbed layout.
const MyApp = StackNavigator({
TestComp1: { screen:TestComp1 }, // This screen will have tabs.
TestComp2: { screen:TestComp2 }
});
AppRegistry.registerComponent('myApp', () => myApp);
Let's now move on to your TestComp1, which is the screen that has your tabs.
TestComp1:
// ... all imports here.
// This should be your first tab.
class Home extends Component {
static navigationOptions = {
// Label for your tab. Can also place a tab icon here.
tabBarLabel: 'Home',
};
render() {
return(
<View style={styles.container}>
<View style={styles.mainContent}>
// This will change your tab to 'Profile'.
<Button
onPress={() => this.props.navigation.navigate('Profile', {name: 'Lucy'})}
title="NewPage"
/>
</View>
</View>
)
}
}
// This can be your second tab and so on.
class Profile extends Component {
static navigationOptions = {
// Label for your tab.
tabBarLabel: 'Profile',
};
render() {
return (
<Text>This is the profile Tab.<Text>
);
}
}
export default TabNavigator({
Home: {
screen: Home,
},
Profile: {
screen: Profile,
},
}, {
// This will get the tabs to show their labels/icons at the bottom.
tabBarPosition: 'bottom',
animationEnabled: true,
tabBarOptions: {
activeTintColor: '#e91e63',
},
});
So essentially, your TestComp1 has two components (Home and Profile) inside it which are both parts of TabNavigator so they'll act as tabs. (You don't need to worry about a separate footer component as ReactNavigation places that automatically using your component's navigationOptions) We'll be exporting this TabNavigator instance that we'll use as an import to index.android.js and we'll place this import inside our StackNavigator as a screen of the app.
This way you've incorporated Tabs as well as StackNavigator inside your RN app. Also, tabBarPosition: 'bottom' places your tabs at the bottom.
You also no longer to maintain a separate FooterTabs component as you can see.
Read up the docs if you need more clarity or fine-tuning.
Hope I've helped. :)

How to get current navigation state

I am using React Navigation's Tab Navigator from https://reactnavigation.org/docs/navigators/tab, when I switch between the Tab Screens I don't get any navigation state in this.props.navigation.
Tab Navigator:
import React, { Component } from 'react';
import { View, Text, Image} from 'react-native';
import DashboardTabScreen from 'FinanceBakerZ/src/components/dashboard/DashboardTabScreen';
import { TabNavigator } from 'react-navigation';
render() {
console.log(this.props.navigation);
return (
<View>
<DashboardTabNavigator />
</View>
);
}
const DashboardTabNavigator = TabNavigator({
TODAY: {
screen: DashboardTabScreen
},
THISWEEK: {
screen: DashboardTabScreen
}
});
DASHBOARD SCREEN:
import React, { Component } from 'react';
import { View, Text} from 'react-native';
export default class DashboardTabScreen extends Component {
constructor(props) {
super(props);
this.state = {};
console.log('props', props);
}
render() {
console.log('props', this.props);
return (
<View style={{flex: 1}}>
<Text>Checking!</Text>
</View>
);
}
}
I get props at Dashboard Screen when it renders the component first but I don't get props when I switch the tabs.
I need to get the current Screen name i.e TODAY or THISWEEK.
Your problem is about "Screen Tracking", react-navigation has an officially guide for this. you can use onNavigationStateChange to track the screen by using built-in navigation container or write a Redux middleware to track the screen if you want to integrate with Redux. More detail can be found at the officially guide: Screen-Tracking. Below is a sample code from the guide by using onNavigationStateChange:
import { GoogleAnalyticsTracker } from 'react-native-google-analytics-bridge';
const tracker = new GoogleAnalyticsTracker(GA_TRACKING_ID);
// gets the current screen from navigation state
function getCurrentRouteName(navigationState) {
if (!navigationState) {
return null;
}
const route = navigationState.routes[navigationState.index];
// dive into nested navigators
if (route.routes) {
return getCurrentRouteName(route);
}
return route.routeName;
}
const AppNavigator = StackNavigator(AppRouteConfigs);
export default () => (
<AppNavigator
onNavigationStateChange={(prevState, currentState) => {
const currentScreen = getCurrentRouteName(currentState);
const prevScreen = getCurrentRouteName(prevState);
if (prevScreen !== currentScreen) {
// the line below uses the Google Analytics tracker
// change the tracker here to use other Mobile analytics SDK.
tracker.trackScreenView(currentScreen);
}
}}
/>
);
Check all properties first, like
<Text>{JSON.stringify(this.props, null, 2)}</Text>
Above json array will show you current state of navigation under routeName index i.e.
this.props.navigation.state.routeName
Have you tried to define navigationOptions in your route object?
const DashboardTabNavigator = TabNavigator({
TODAY: {
screen: DashboardTabScreen
navigationOptions: {
title: 'TODAY',
},
},
})
You can also set navigationOptions to a callback that will be invoked with the navigation object.
const DashboardTabNavigator = TabNavigator({
TODAY: {
screen: DashboardTabScreen
navigationOptions: ({ navigation }) => ({
title: 'TODAY',
navigationState: navigation.state,
})
},
})
Read more about navigationOptions https://reactnavigation.org/docs/navigators/
Answer as of React Navigation v6
Depending on whether you want to trigger re-renders on value changes:
const state = navigation.getState();
or
const state = useNavigationState(state => state);
Reference:
https://reactnavigation.org/docs/use-navigation-state#how-is-usenavigationstate-different-from-navigationgetstate

Resources