i18n translation for "Back"-text in React Navigation on iOS - react-native

I'm using React Navigation and a StackNavigator in a React Native app. I'm able to translate the navigationOptions.title, however, on iOS if there is too much text in the header it defaults to showing the text "Back" next to the back button. I don't mind this, but I want it to say "Back" in my current language. How can I achieve this? The text I'm talking about:
An example of how I use the navigationOptions now:
class Example extends Component<Props> {
static navigationOptions = () => ({
title: i18n.t('example_title'),
});
// ...
}
In case it is relevant we're using react-native-localize with i18n-js for the i18n functionality. I don't want it to always say the previous screens name, or always back, I want it dynamically as it is now, just with i18n.

The navigationOptions object has an key for this called headerTruncatedBackTitle.
Title string used by the back button when headerBackTitle doesn't fit on the screen. "Back" by default. headerTruncatedBackTitle has to be defined in the origin screen, not in the destination screen.
You can for example utilize this with i18n simply by doing:
class Example extends Component<Props> {
static navigationOptions = () => ({
title: i18n.t('example_title'),
headerTruncatedBackTitle: i18n.t('example_back'), // "Back", "Zurück", etc.
});
// ...
}

In my application in Main AppStack createStackNavigator where you combine all of your screens, I have added a second parameter an object in which we give it a key
createStackNavigator({
...screens
},
{
defaultNavigationOptions: {
headerBackTitle: i18n.t('example_title')
}
})
This will set the Back button to the current language of the entire application.

Related

Dynamic titles with function components

Recently I started rewriting some of my legacy code that used class components to modern hooks. The issue I have is that I used this to set header title like so:
static navigationOptions = ({ navigation }) => ({
title: someVar,
})
Now, I have to do it like so:
MyScreen.navigationOptions = ({ navigation }) => {
return {
title: navigation.getParam('headerTitle'),
}
}
and then
useEffect(() => {
navigation.setParams({
headerTitle: 'Some title',
})
}, [])
Which works fine with static screen titles. But for dynamic titles it does not. It takes a second to update the title, first it renders with empty title. Which is explainable, given the method. It worked perfectly with class components. Is there a better way to do this?
You are setting the params (setParams) passed to the screen and not the options (setOptions) of the screen itself which is what's causing the odd behaviour you are experiencing.
I am not sure what your use case is so I can't tell you which one to use but there are two ways to set the title in react navigation. Either from the navigator using the options parameter or from inside the component using navigation.setOptions take a look at this https://reactnavigation.org/docs/headers

MaterialTopTabNavigator dynamic route configs

I want to create via createBottomTabNavigator. It has 5 tabs. Each tab is a StackNavigator.
One of these tabs has a top tab bar. I create the top tab bar via createMaterialTopTabNavigator
But I know tab count after http request. How can I add tab dynamically? The doc says that
There are workarounds if you absolutely need dynamic routes but you can expect some additional complexity
I am confused about this task.
How can I do that?
Related react-navigation issue: https://react-navigation.canny.io/feature-requests/p/dynamic-routes-for-navigators
I think you can create a component that returns a tabNavigator. You can then access props or do whatever you want to dynamically add or remove tabs. Here I am using the latest version of react-navigation.
import React, { Component } from 'react-native';
import { createAppContainer, createMaterialTopTabNavigator } from 'react-navigation';
class DynamicTabs extends Component {
render() {
// I am using a prop here to update the Tabs but you can use state to update
// when the network request has succeeded or failed
const { shouldRenderTab } = this.props;
const TabNavigator = createMaterialTopTabNavigator({
Tab1: Tab1Component,
Tab2: Tab2Component,
// Create a tab here that will display conditionally
...(shouldRenderTab ? { Tab3: Tab3Component } : {}),
});
const ContainedTabNavigator = createAppContainer(TabNavigator);
return <ContainedTabNavigator />;
}
}
export default DynamicTabs;
This is the current solution I am using adapted from the original solution posted on github

Localization of React Native navigators

I am building an app where the users are prompted to choose their language the first time they launch the app. The language is then stored locally using AsyncStorage.
Every time the app launches the language is retrieved from storage and saved into the global.lang variable to be used by all components:
AsyncStorage.getItem('settings', (err, item) => {
global.lang = item.lang;
});
When I use the global.lang variable in the render() method in any component everything seems to be ok. However I run into trouble when trying to use the same variable when initializing my navigators:
const TabNavigator = createBottomTabNavigator(
{
Home: {
screen: HomeScreenNavigator,
navigationOptions:{
title: strings['en'].linkHome, --> this works
}
},
News: {
screen: NewsScreen,
navigationOptions:{
title: strings[global.lang].linkNews, --> this fails
}
}
});
I believe that this because the value is not retrieved from AsyncStorage by the time that the navigators are constructed. If I set the global.lang manually (eg. global.lang = 'en';) it seems to be OK, but not when I try to retrieve it from the storage.
Is there something that I am missing? Could I initialize the navigator with a default language and change the title later based on the value retrived?
Any help would be greatly appreciated.
The navigators are constructed in the app launch. So you would need to use some placeholder text and use the method described here where you change all screen titles based on the screen key...
Or... this sounds insane and i have never tried it. But you can use a loading screen where you retrieve the languaje settings. then... via conditional rendering you "render" a navigator component . Idk if it would work the same way , but you can try it. below some code that i just created for this purpose
export default class MainComponent extends React.Component {
constructor(props) {
super(props);
this.state = { hasLanguage:false};}
componentDidMount(){
this.retrieveLanguage()
}
async retrieveLanguage(){
//await AsyncStorage bla bla bla
//then
this.setState({hasLanguage:true})
}
render() {
return (
{
this.state.hasLanguage?
<View>
//this is a view that is rendered as a loading screen
</View>:
<Navigator/>//this will be rendered, and hence, created, when there is a language retrieved
}
);
}
}
Again. I don't know if react navigation creates the navigator at render . If so. When it creates a navigator , there should be the languaje to be used there

React Native StackNavigator Title Change Causes Recursion

I've split out the navigationOptions to the top of each screen to make things easy to understand, rather than keeping it in the TabNavigator. Here's an example of one screen:
static navigationOptions = ({navigation}) => {
const {params = {}} = navigation.state;
return {
title: params.title != undefined ? `${params.title}` : "Step 1: Select a Letter",
tabBarOnPress: ({...props, scene})=>{
params.onFocus();
},
titleStyle: {
textAlign: 'center'
}
}
};
Once a user selects a letter on this screen, I switch the view to show a different component (a list of items that start with that letter, for example).
I'd like to change the title of the TabNavigator Header to now show something like "Step 2: Select an item from the list". I saw on a thread that you can call a method from the method that rebuilds the view like this:
_letterSelectedHandler = (letter) => {
this._changeTitle("Step 2: Select an item...");
...
}
With the method:
_changeTitle = (title) => {
this.props.navigation.setParams({title});
}
Unfortunately, it seems to create an infinite loop. Is there a better way of simply changing the TabNavigator Header Title?
I should mention that I'm not using Redux or anything too complex, since I'm new to React Native. Please let me know if I can provide any more information to help illustrate the issue I'm having.

React Navigation cache component

I am fairly new to React Native and a little confused with the following. I've set up a StackNavigator as shown
const MyProjectNavigator = StackNavigator({
home: {screen: Other},
latest_news: {screen: LatestNews}
}
When I want to go to a different screenm the navigation I perform is:
navigate(
latest_news, {
otherParams : param1
}
);
This works well so far.
Now, assume that the latest_news component queries a lot of data from a server when mounted, then performs lots of operations on that data, sorting by date, author, yadda yadda. This takes some time to complete.
How would you suggest I made this faster? On iOS for example i would normally keep my ViewController in memory and if it was available, display that. When using navigate(), the navigator seems to create a new instance of the component thus reloading and re-processing everything from the beginning making the users wait every time.
*TL;DR
I want to keep all the data my component has queried and processed across navigation so that the processing doesn't have to repeat constantly.
I could just put the data on the global object but that doesn't sound like a good solution
Thanks a bunch.
You can use the TabNavigator instead of the StackNavigator.
TabNavigators reuse the same instances of its route items.
you can disable the visibility of the TabBar:
const TabNav = TabNavigator({
Home: { screen: HomePage},
NewsFeed1: { screen: NewsFeed} ,
NewsFeed2: { screen: NewsFeed} ,
NewsFeed3: { screen: NewsFeed} ,
// ...
},
{
navigationOptions: ({ navigation }) => ({
tabBarVisible: false,
}),});
and then you can navigate manually - for example with a button:
<Button title="NewsFeed1" onPress={() => this.props.navigation.navigate("NewsFeed1") }/>
Have a look at a simple example:
https://github.com/chrisdie/AwesomeNavigation/blob/master/src/tabbar2/App.js