I have Question. How can I pass specific param from each screen to the StackNavigator header in order to come out different kind of components when reached the screen.
I have done some research about this kind of question, but there are not much info that can help me. So I posted here to find some help, hope there are someone who can guide me. Thanks a lot.
const mainNav = TabNavigator({
Home: {
screen: HomeScreen,
param:{
tabval:1
}
},
Live: {
screen: LiveScreen,
param:{
tabval:2
}
},
Radio: {
screen: RadioScreen,
param:{
tabval:3
}
},
} );
function DifferentComponents(tabval){
if(tabval == 1){
//do something
}else if(tabval == 2){
//do something
}else{
//do something
}
}
export const mainStack = StackNavigator({
Home: {
screen: mainNav,
navigationOptions: {
header: (
<View style={styles.topnavscrollview}>
<View style={{width:300}}>
<ScrollView horizontal={true} showsHorizontalScrollIndicator={false}>
{this.DifferentComponents(tabval)} <-- Change this when switch to Live tab or others
</ScrollView>
</View>
</View>
),
},
},
Content: { screen: ContentScreen },
});
You can pass in the custom header value as a prop to the component.
Then put something like this at the top the component that you want to customize the header for:
class Home extends React.Component {
// dynamically set header title when component mounts
static navigationOptions = (props) => {
const title = props.myTitleForThisComponent;
return { title }
}
render(){
// render stuff..
}
}
When you navigate to the component via a StackNavigator link, any props that you pass into the component will be available as this.props.navigation.state.params.
For example, if you navigate to your component via StackNavigator like this:
this.props.navigation.navigate(
'Home',
/* below passes into the "Home" component as: this.props.navigation.state.params.title */
{ myCustomTitle: "hello there" }
)}
Then you can create a custom title for the Home page component like this:
static navigationOptions = ({ navigation }) => {
const { myCustomTitle } = navigation.state.params;
return { title: `${myCustomTitle} !!`}
}
hello there !!
Note: when you define your StackNavigator, you do not need to include the option navigationOptions.title, since you are add it dynamically when the component mounts.
However, you can provide generic navigationOptions values within the StackNavigator definition, to provide a "default" values for components that do not add/re-write them dynamically.
Related
I wanna know the best way to set the params and options for react native navigation in a class component.
note that the same params are used in options.
when I put all code in the constructor I got params undefined because of timing issue.
and it works. for me in one case when I added option in componentDidMount , I will write some examples in the code below.
1- first case using class component (it's working)
type Props = {
navigation: NavigationProp<any>;
route: RouteProps<{ Example: {title: string} }, 'Example'>
}
export default class Example extends Component <Props> {
constructor(props: Props){
super(props)
this.props.navigation.setParams({ title: 'title' });
}
componentDidMount(){
this.props.navigation.setOptions({ title: this.props.route.params.title })
}
...
}
2 - second case using FC: (not using this example but I think it's also the best way todo for the FC).
export function Example: React.FC = () => {
const navigation = useNavigation();
const route = useRoute();
useLayoutEffect(()=>{
navigation.setParams({ title: 'title' });
navigation.setOptions({ title: route.params.title })
})
...
}
so I hope my question is clear, is that theright way to set Header options with the lates Navigation on React Native?
constructor is the first step in component lifecycle, and you are setting params inside that, which means there is a prop that is going to be updated.
so we need a function that understands every update on a state or received props, and that listener is nothing except "componentDidUpdate(){}" 🤟:
import {NavigationProp, RouteProp} from '#react-navigation/native';
import React, {Component} from 'react';
import {Text, StyleSheet, View} from 'react-native';
type Props = {
navigation: NavigationProp<any>;
route: RouteProp<{Example: {title: string}}, 'Example'>;
};
export default class Example extends Component<Props> {
constructor(props: Props) {
super(props);
this.props.navigation.setParams({title: 'title'});
}
componentDidUpdate() {
this.props.navigation.setOptions({title: this.props.route.params.title});
}
render() {
return (
<View style={styles.container}>
<Text style={styles.textStyle}>Use component did update :)</Text>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
padding: 30,
},
textStyle: {
color: 'black',
fontSize: 20,
fontWeight: 'bold',
},
});
I am having an issue when I navigate from Home component that contains a list of companies and I press in a button to load the Company view, the data is being loaded but when I press back in Android is returning to Home and when I press in a different company button to load its details, is rendering the same view with the same previous data, that means, the component is not being updated/unmounted.
These are my routes
const drawerConfig = {
initialRouteName: 'Home',
contentComponent: SideMenu,
drawerWidth: width,
}
const MainDrawerNavigator = createDrawerNavigator(
{
Home: {
screen: Home,
},
Company: {
screen: Company,
},
Gifts: {
screen: Gifts,
},
Contact: {
screen: Contact
}
},
drawerConfig,
);
const InitialStack = createStackNavigator(
{
Menu: {
screen: Menu,
path: 'menu/',
}
},
{
initialRouteName: 'Menu',
headerMode: 'none',
}
);
const SwitchNavigator = createSwitchNavigator(
{
Init: InitialStack,
App: MainDrawerNavigator,
},
{
initialRouteName: 'Init',
}
);
const AppContainer = createAppContainer(SwitchNavigator);
export default AppContainer;
I am navigating from Home to Company with this
goToCompany = company => event => {
this.props.navigation.navigate('Company', {
company,
});
}
And receiving the params in Company with this
componentWillMount() {
this.setState({ companyData: this.props.navigation.getParam('company', {}) });
}
So I am expecting the Company component will unmount on pop or allow me to change the state of the Company component when I send the details from Home.
I am using react-navigation 3.5.1 and react-native 0.59.3
React native navigation does not work as web. Read here for more details. https://reactnavigation.org/docs/en/navigation-lifecycle.html
When you navigat to other screen it doesn't actually get unmounted. Read documentation for details.
Use willFocus instead.
you can try using componentDidUpdate check in docs
componentDidUpdate(prevProps) {
// Typical usage (don't forget to compare props):
if (this.props.userID !== prevProps.userID) {
this.fetchData(this.props.userID);
}
}
You need to use the push method of navigation object. The push replace the current route with a new and the navigate search a route and if not exist create new one.
I have a react-native application whose top level App.js is as follows:
const AuthStack = createStackNavigator(
{ Landing, SignUpWithGoogle },
{
initialRouteName : 'Landing'
, navigationOptions : {
header : null
}
}
);
const AppStack = createStackNavigator(
{
AppContainer
, Campaign
, Compose
},
{
initialRouteName : 'AppContainer'
, navigationOptions : {
header : null
}
}
);
/**
#TODO:
force app to reload on user creation ...
there needs to be a state transition on user creation
... not sure how to
*/
class App extends React.Component {
render () {
const stack = (!this.props.authenticating && this.props.user)
? <AppStack {...this.props} />
: <AuthStack />
return (
<Loader
isLoaded = { !this.props.authenticating && this.props.feedIsLoaded}
imageSource = {logo}
backgroundStyle = {loading.loadingBackgroundStyle}
>
{stack}
</Loader>
)
}
}
There is a Loader component from https://github.com/TheSavior/react-native-mask-loader that hides the app with a splash page until the app has fetched user authentication data, or determined that this is a new user. This works great except in the case when the user signs up for the first time, then the app just jumps right into authenticated mode, and fails to load initial data that I want for the new user. The only to get around this is to restart the app, which is a no go. Is there a way to transition between AuthStack and AppStack better so that the information I want on signup are all there? One way would be to force reload the app so that we are directed back to the Loader screen and state, but it's unclear how this can be done.
I did something like this for my use case.
const Routes = {
Login: {
screen: Login
},
Status: {
screen: Status,
navigationOptions: {
gesturesEnabled: false, // To prevent swipeback
},
},
...
};
const LoggedRootStack = createStackNavigator(
{
...Routes
},
{
initialRouteName: 'Status'
}
)
const RootStack = createStackNavigator(
{
...Routes
},
{
initialRouteName: 'Login'
}
)
For passing props use Redux for keeping a global state. You can connect your components to redux state to access the props. Hope this helps
I have a screen inside my react-navigation StackNavigator that looks like this:
import React from 'react';
import { FlatList, ScrollView, StyleSheet, Text, View } from 'react-native';
import { List, ListItem } from 'react-native-elements';
import Accordion from '#ercpereda/react-native-accordion';
export default class PassportScreen extends React.Component {
static navigationOptions = {
title: 'Passport Recovery',
};
constructor(props) {
super(props);
this.renderItem = this.renderItem.bind(this);
}
renderItem(item) {
return (
<View>
<Accordion
header={item.item.key}
content={item.item.content}
/>
</View>
)
}
render() {
const instructions = [
{
key: <Text>1. Fill out a passport application form</Text>,
content: <Text>Content</Text>
},
{
key: <Text>2. Fill out a lost/missing passport statement</Text>,
content: <Text>Content</Text>
},
///...etc
];
return (
<ScrollView>
<FlatList
data={instructions}
renderItem={this.renderItem}
/>
</ScrollView>
)
}
}
module.exports = PassportScreen;
however, when I click to navigate to this screen from another screen, I get this error: TypeError: this.props.header is not a function. (In 'this.props.header({
isOpen: this.state.is_visible
})', 'this.props.header' is an instance of Object).
Other questions I've looked at with similar errors have mentioned passing props to the constructor and needing to pass this.renderItem instead of this.renderItem(), both of which I have already done, so I'm wondering if the problem comes from the fact that this screen is inside a StackNavigator and is navigated to by clicking on a ListItem. Is my intuition correct? If so, how can I fix this?
It seems that the header prop accepts a function, rather than just a component like content does.
Right now you're directly passing an object to the header prop, therefore it won't accept the callback function.
You may try the following approach in order to pass a callback to the header.
PassportScreen.js
customFunc = (callback) => {
console.log(callback)
}
renderItem = (item) => { // Useful to bind `this`
return (
<View>
<Accordion
header={this.customFunc}
content={item.item.content}
/>
</View>
)
}
ChildComponent.js
this.props.header('I'm setting the callback here')
I've been trying to call a function which is in a screen of a navigator from its screen.
To clarify the point, here is a snippet of my code...
//ScreenA.js
export default class ScreenA extends React.Component {
showWarning(){
this.setState({showWarning: true});
setTimeout(function() {
this.setState({showWarning: false});
}.bind(this), 3000);
}
render() {
return (
<View style={{backgroundColor : this.state.showWarning ? "#red" : "#blue"}}>
{this.state.showWarning && <Warning />}
</View>
)
}
}
//Nagigator.js
default export const Navigator = StackNavigator({
ScreenA: {screen: ScreenA},
ScreenB: {screen: ScreenB},
});
//App.js
export default class App extends React.Component {
handleSubmit(qrCode){
if(qrCodeIsInvalid){
this.navigator.ScreenA.showWarning();
//This is just a sudo code.
//How do we call ScreenA.showWarning here?
}
}
render() {
let props = {/*some props here*/};
return (//This "ref" stops the application without describing any reason
<Navigator screenProps={props} ref={nav => { this.navigator = nav; }}/>
);
}
}
There is an example of how to call a function from a navigation header, but not from the class which exports the navigator.
I thought that each screen can be accessed via ref, but this causes an error without explaining what's happening.
Has anyone encountered a similar situation?
Any advice will be appreciated.
P.S.
# Nimrod Argov
Here are details of what I've been trying to achieve.
ScreenA has a QR code reader and submit function, which submits QR codes to App.js.
App.js has handleSubmit function, where submitted QR codes are sent to a server and labelled as either valid or invalid.
If a submitted QR code turns out to be invalid, ScreenA has to show a warning message and change its background colour for 3 seconds.
It might be achieved by having App.js pass a prop {showWarning:true} to ScreenA and pass {showWarning:false} in 3 seconds.
However, I thought it would be ScreenA's responsibility to change its background colour. Thus, I set setTimeout and setState in the showWarning().
I did it this way:
ScreenA.js
While navigate to ScreenB, including the function you want to call.
export default class ScreenA extends React.Component {
constructor(props) {
super(props);
this.doSomething = this.doSomething.bind(this);
}
doSomething() {
this.setState({blah: true});
}
navigateToB() {
this.props.navigation.navigate('ScreenB', {
doSomething: this.doSomething,
});
}
}
ScreenB.js
So you can do this in ScreenB.
const { state, setParams, navigate } = this.props.navigation;
const params = state.params || {};
params.doSomething();