this.props.navigation.navigate() is undefined - react-native

I am using createStackNavigator like this.
const ChatStack = createStackNavigator({
Profiles: Profiles,
ChatScreen: ChatScreen
})
const RootChatStack = createStackNavigator({
Home: ChatStack,
ModalScreen: ProfileModalScreen
},{
mode: 'modal',
headerMode: 'none',
})
In Profiles I use props.navigation.navigate('ChatScreen',{name: item.content }) to go to ChatScreen.
And In ChatScreen
class Example extends React.Component {
static navigationOptions = ({navigation}) => {
return {
title: navigation.getParam('name', ''),
};
...
renderModal() {
this.props.navigation.navigate('ModalScreen')
}
...
}
I can set title using navigationOptions, but when I use this.renderModal(),
It gives me Error
undefined is not an object (evaluating '_this2.props.navigation')
How I can use navigation.navigate inside Example Component?

If you're using class based react components you have to bind additional functions in your components to the instance. You would do so by explicitly binding this to each additional function in your component (react functions like render() are already bound).
This is the key line in the snippet below:
this.renderModal = this.renderModal.bind(this);
class Example extends React.Component {
constructor(props) {
super(props);
this.renderModal = this.renderModal.bind(this);
}
renderModal() {
this.props.navigation.navigate('ModalScreen');
}
...
}

Related

react-navigation: access stateChange event of nested navigator

I'm trying to add a state change event to a nested navigator.
Usually I would define it like this:
const TabNavigator = createMaterialTopTabNavigator({
Home: {
screen: Home,
},
Next: {
screen: NextNavigator,
},
})
but I need something like this:
const TabNavigator = createMaterialTopTabNavigator({
Home: {
screen: Home,
},
Next: {
screen: (props)=>{ return <NextNavigator {...props}/> },
},
})
where the screen object is a function.
The problem is I have no idea what signature screen as a function has, and how to pass it to the navigator
I'm getting close with something like this:
screen: (props, state)=>{ return <NextNavigator {...props} state /> },
But it's still not correct.
Does anyone know how this works?
If you don't want #JeffGuKang to answer, you can declare and use global variables and functions.
let NICKNAME = '';
function setNickName(data) {
NICKNAME = data;
}
function getNickName() {
return NICKNAME;
}
export { setNickName,getNickName }
...
//set data
import { setNickName} from './globalpath';
setNickName(data)
....
//get data
import { getNickName} from './globalpath';
getNickName()
Use Params
Use setParams, getParam for changing state in components wrapped by navigatior.
A Component (Set changing value)
const name = this.props.navigation.setParams({ name: this.state.name, age: this.state.age });
B Component (Get value)
const name = this.props.navigation.getParam('name', 'DefaultValue');
const age = this.props.navigation.getParam('age' 0);
setParams is a trigger to run getParam in other components is wrapped navigation.
Extend Navigator
You can extend the component created by Naivgator as below. link
const MyStack = createStackNavigator({ ... });
class CustomNavigator extends React.Component {
static router = MyStack.router;
state = {
a: 'b',
}
render() {
const { navigation } = this.props;
return <MyStack {...this.state, ...this.props} />;
}
}
Or it is able to use functional component instead.
const MyStack = createStackNavigator({ ... });
CustomNavigator = (props) => {
return <MyStack {...props} />
}
const TabNavigator = createMaterialTopTabNavigator({
Home: {
screen: Home,
},
Next: {
screen: (props) => { return <MyStack {...props} /> },
},
Third: CustomNavigator,
})
And do not forget import `React` to use functional component.

How do i switch screens from one file to another

I'm very new to react native so please explain carefully.i have 3 files App.js SplashAndLogin.js and Register.js Im able to get from the initial screen to the register screen and go back and forth between my components in my Register file but when its time to go back to the Login screen i always seem to get the same error.
I've tried several different things but they all keep giving me the same error. im starting to think the way i set up my files are just wrong.
//App.js
class App extends Component<Props> {
render() {
return (
<AppContainer/>
)
}
}
export default App
const AppSwitchNavigator = createSwitchNavigator(
{
Login: {screen: SplashAndLogin},
//Registe :{screen: Register}
});
//SplashAndLogin.js
class SplashAndLogin extends Component<Props> {
render() {
return (
<AppContainer/>
)
}
}
export default SplashAndLogin;
const SAndLAppNavigator = createSwitchNavigator(
{
SandL : {screen: LoadingScreen },
RegisterScreen : {screen: Register}
}
);
//Register.js
export default class Application extends Component<Props> {
render() {
return (
<AppContainer/>
);
}
}
const AppSwitchNavigator = createStackNavigator(
{
Login :{screen: NameScreen},
PhoneAndEmail: {screen: EmailPasswordScreen},
HomeScreen: {screen: SplashAndLogin },
UploadScreen: {screen: CertificateUploadScreen }
});
const AppContainer = createAppContainer(AppSwitchNavigator);
So to summarize i can get to every screen except when im on the Register.js file and try to navigate to the HomeScreen it throws out the error
"The component for route 'HomeScreen' must be a react component For example...."
It's normal, you are using two different AppContainer.
You can not call a view in another AppContainer from the current AppContainer
this.props.navigation
refers to the current navigation
Use the state to change AppContainer
class RenderAppContainer extends Component {
constructor(props) {
super(props);
this.state = { IsConnected : false }
this.changeIsConnected = this.changeIsConnected.bind(this)
}
changeIsConnected = () => {
this.setState((prevState, props ) => ({ IsConnected : !prevState.IsConnected}))
}
render() {
cons {IsConnected } = this.state
return(
<React.Fragment>
{(IsConnected) ? AppContainerOne : AppContainerTwo } 
</React.Fragment
)
}
}
You can then pass changeIsConnected to your AppContainer
<AppContainerOne screenProps={{changeConnected: this.changeIsConnected}} />
<AppContainerTwo screenProps={{changeConnected: this.changeIsConnected}} />
https://reactnavigation.org/docs/en/stack-navigator.html
see documentation for ScreenProps

pass props in nested stackNavigator

I'm a beinner in React-Native and i'm using stackNavigator from react-navigation package to create a Wizard. But i can't pass parameters between different screens of stacks:
class TaskWizard extends React.Component {
constructor(props) {
super(props)
let {service} = this.getNavigationParams()
this.state = {
model : {service.fields}
}
}
getNavigationParams() {
return this.props.navigation.state.params || {}
}
}
const TaskWizardStack = createStackNavigator({
Wizard : {
screen : TaskWizard,
},
})
const PostTaskStack = createStackNavigator({
// some screens here then Task wizard
Deep : {
screen : Deep
}
TaskWizard : {
screen : TaskWizardStack
}
})
export default PostTaskStack
I know i can pass parameters to a screen like this from Deep component :
<View>
<TouchableOpacity
onPress={() => this.props.navigation.navigate('TaskWizard', {
service: {id : 1, fields : [{}]} },
})
/>
</View>
This will pass service parameter from inside of Deep component to TaskWizard Stack. I need to pass it from TaskWizard to every child screens like Wizard. Is it possible?
Try using screenprops to pass the data to other components
https://reactnavigation.org/docs/en/stack-navigator.html#navigator-props
Code below works for me
export const AdminBottomNavigator = createBottomTabNavigator(
{Home: {screen: SchoolDashboard},
Students: {screen: StudentList},
Message: {screen: Dashboard},
Settings: {screen: StudentList},},
defaultNavigationOptions: ({ navigation }) =>
({tabBarIcon: ({ focused, horizontal, tintColor }) =>
{let screenProps=navigation.getScreenProps();}})})

React Navigation: Share state between two screens within TabNavigator

I'm learning react native by building a simple chat app. I have two screens wrapped in a TabNavigator where the first screen (Screen A) being the chatbox, and the other screen (Screen B) which displays a list of online users. I'm using SocketIO to fetch these users.
Problem is, how can I access the "onlineUsers" state from ScreenA to ScreenB so I can see an updated list of online users whenever I receive a "user joins" event?
Screen A:
export default class ScreenA extends Component {
constructor(props) {
super(props);
this.state = {
onlineUsers = [];
}
}
componentDidMount() {
// Update list of online users when new user joins chat
this.socket.on('user joins', (payload) => {
this.setState({
onlineUsers: payload.users
})
})
}
}
Screen B:
export default class ScreenB extends Component {
constructor(props) {
super(props);
// I want to get the onlineUsers from ScreenA
this.state = {
onlineUsers = [];
}
}
}
Router:
export const Chat = TabNavigator({
ChatBox: {
screen: ScreenA
},
OnlineUsers: {
screen: ScreenB
},
})
PS: I'm using react-navigation to handle navigation
Best way is to handle events in the parent component and then passing it to their children components. So in your case, you should have a online user list in your router. Then pass the array to screen B. Here is how you should do
Router
state = {
online_users:[]
}
_update = (data) => {
this.setState({online_users:data});
};
export const Chat = TabNavigator({
ChatBox: {
screen: <ScreenA onUpdate={this._update}/>
},
OnlineUsers: {
screen: <ScreenB userList={this.state.online_users}>
},
})
Screen A
export default class ScreenA extends Component {
constructor(props) {
super(props);
}
componentDidMount() {
// Update list of online users when new user joins chat
this.socket.on('user joins', (payload) => {
this.props.onUpdate(payload.users)
})
}
}
Screen B
export default class ScreenB extends Component {
constructor(props) {
super(props);
}
// You can access online user using this.props.userList
}
I came across this post when I ran into a similar issue earlier this year so I thought I'd post my solution.
I would say your best bet in this situation is to use a Custom Navigator to wrap your TabNavigator which will expose <TabNavigator /> in your custom navigator allowing you to pass any methods or state down to ScreenA and ScreenB as screenProps.
The custom navigator would look like:
import React from 'react'
import { ChatTabNavigator } from './ChatTabNavigator'
class StateManagingCustomNavigator extends React.Component {
static router = ChatTabNavigator.router
state = {
onlineStatus: [theStatus]
}
handleMessagePosted = () => {
// Handle message post, etc...
}
// ... SocketIO code and other logic to manage the state here? ...
render() {
const { navigation } = this.props
const { onlineStatus } = this.state
return (
<ChatTabNavigator
screenProps={{
theStatus: onlineStatus,
chatMessagePosted: this.handleMessagePosted
}}
navigation={navigation}
/>
)
}
}
export default StateManagingCustomNavigator
From here you could implement an event system as #Ashish Prakash suggested, or manage all of your state in the custom navigator and transform ScreenA and ScreenB into presentational components.
Use this.props.navigation.setParam({onlineUsers: onlineUsers: payload.users}) when you get user-list from server.
Then use it in Screen B like this.props.navigation.state.params.onlineUsers

How to send parameters from screen to stack navigator?

I am a newbie to react native.
I am using a stack navigator inside a tab navigator; I have to navigate multiple screens inside each tab. i am able to send parameters from my default class HomePage to my nested classes in my tab and stack navigators.
export const MyApp = TabNavigator({
Asset: {
screen: AssetScreen,
},
Sensors: {
screen: sensorsStack,
},
Settings: {
screen: settingStack
},
}
export const sensorsStack = StackNavigator({
sensors : { screen: SensorScreen },
sensorDetails : { screen: SensorDetails }
});
export const settingStack = StackNavigator({
settings: { screen: SettingsScreen },
about : { screen: About },
environment : { screen: Environment }
});
export default class HomePage extends Component {
constructor(props) {
super(props);
this.state = {
assetID:'xyz',
authToken:'xyzz'
}
}
static navigationOptions = {
header: null
};
render() {
const screenProps = {
asset: {
assetID: this.state.assetID,
authToken : this.state.authToken,
},
}
return (
<MyApp screenProps={screenProps} />
);
}
}
Now, i want to send a parameter from 'SensorScreen' to 'SensorDetails'. I have tried sending parameters using
NavigationActions.navigate({ routeName: 'sensorDetails' ,params: { sensorType:'Fuel',}});
from 'SensorScreen' class. But was not able to get the parameter in 'SensorDetails' class. How can i pass this params?
A little late answer but might help others that ends up here.
Instead of using NavigationActions you could try navigation from sensorScreen to sensorDetails with navigate and passing some extra variables.
this.props.navigation.navigate('SensorDetails',{sensorType:'Fuel'})
The passed object can then be read in the second screen by
this.props.navigation.state.params.sensorType
A minimal example of the case can be seen in these lines. Observe that this is a stupid on icon tab bar. But it is kept that way since the question was about a tab bar with stacks on each tab. And new tabs are easily added.
import React,{Component} from 'react'
import { Text, View, Footer,Button } from 'react-native'
import {StackNavigator,TabNavigator} from 'react-navigation'
export class SensorScreen extends React.Component {
render() {
return (<Button
onPress={() => this.props.navigation.navigate('SensorDetails',{sensorType:'Fuel'})}
title="Go to Sensor Details"
/>)}}
export class SensorDetails extends React.Component {
render() {
return (<View>
<Text>{this.props.navigation.state.params.sensorType}</Text>
</View>);}
}
const sensorsStack = StackNavigator({
sensors : { screen: SensorScreen },
SensorDetails : { screen: SensorDetails }
});
const MyApp = TabNavigator({
Sensors: {
screen: sensorsStack,
},
});
export default class Nested extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<MyApp/>
);
}
}
Hope this helps.