How can you push a screen using only the navigation ref - react-native

Using the react-navigation library, when you're on a screen that's part of a stack navigator you can use props.navigation.push(route name, params). I want to achieve the same thing, being able to push a new screen but from the root level where I take the NavigationContainer ref.
So I have this code
<NavigationContainer ref={navigationRef}>
...
</NavigationContainer>
And I want to do something like
navigationRef.push(routeName, params);
Unfortunately, I don't have the push method here. Is there a workaround for this?

You could set up a RootNavigation.js file like this:
import * as React from 'react';
import { StackActions } from '#react-navigation/native';
export const navigationRef = React.createRef();
export function push(...args) {
navigationRef.current?.dispatch(StackActions.push(...args));
}
Then you can use it like this:
import {navigationRef} from './path/to/RootNavigation';
<NavigationContainer ref={navigationRef}>
{/* content */}
</NavigationContainer>
and this:
import * as RootNavigation from './path/to/RootNavigation';
RootNavigation.push('Screen', { data: '...' })
Source: https://reactnavigation.org/docs/navigating-without-navigation-prop/.

Related

unable to resolve module path in login in src

[
import React from 'react'
import { NavigationContainer } from '#react-navigation/native'
import Login from './android/app/src/Login'
import flexbox from './android/app/src/flexbox'
const Stack = createStackNavigator()
function MyStack() {
return(
<Stack.Navigator>
<Stack.Screen
name = 'Login'
component= {Login}/>
<Stack.Screen
name = 'flexbox'
component={flexbox}/>
</Stack.Navigator>
)
}
export default function App(){
return(
<Navigation Container>
<My Stack/>
</Navigation Container>
)
}
](https://i.stack.imgur.com/tYK2z.jpg)
originmodulepath "c:\users\hp\Downloads\task\App.js","targetModuleName"::./src/Login","message"."Unable to resolve module
Instead of writing full path I think you should write the path related to the current location of your file. I mean, if your file is located in /android/app/src and you need import Login that is located in the same directory then a correct import could be
import Login from './Login'

How to apply google fonts to entire Expo/RN app?

I am trying to use Google Fonts with my entire expo project, but i dont want to import it to each and every component. How can I just import it once, and call it everywhere? Or better yet, can I set my imported font as a default font?
app.js
import { Provider } from 'react-redux'
import store from './redux/store'
import { NavigationContainer } from '#react-navigation/native';
import { createNativeStackNavigator } from '#react-navigation/native-stack';
import { useFonts, Montserrat_400Regular,} from '#expo-google-fonts/montserrat';
import { HomeScreen } from './components/HomeScreen';
import { AddItemScreen } from './components/AddItemScreen';
import { DetailedDayView } from './components/DetailedDayView';
export default function App(navigation) {
const Stack = createNativeStackNavigator()
let [fontsLoaded] = useFonts({
Montserrat_400Regular,
});
if (!fontsLoaded) {
return null;
}
return (
<Provider store={store}>
{/* <SafeAreaProvider> */}
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen
name="Home"
component={HomeScreen}
/>
<Stack.Screen
name="AddItemScreen"
component={AddItemScreen}
/>
<Stack.Screen
name="DetailedDayView"
component={DetailedDayView}
/>
</Stack.Navigator>
</NavigationContainer>
{/* </SafeAreaProvider> */}
</Provider>
);
}
For React Native CLI:
First of all, download any fonts in .ttf format.
Create a directory and name it assets, inside the assets directory create another directory name it fonts, and then move the .ttf file here. Your path will look like this: - root/assets/fonts/your-font.ttf
Now, in your root directory create a file named, react-native.config.js. Inside that file paste the following code:
module.exports = {
assets: ['./src/assets/fonts'],
}
Run this command npx react-native-asset. For the older version of React Native try this command npx react-native link
There you go. You're done. Now, check this directory root/android/app/src/main/assets/fonts to check if the font is added or not.
Now you can use your font anywhere in the app. Like this: -
fontFamily: 'your-font'
Note: You can add multiple font-family. For adding another font-family repeat 1, 2, and 4.
For Expo:
For Expo follow this answer: How to apply custom fonts to whole project, Expo React Native

Cannot create custom StackNavigator - "Couldn't register the navigator"?

I'm trying to create a custom StackNavigator and distribute it on a custom package. The problem is that when I want to apply my custom Stack I get the following error:
Error: Couldn't register the navigator. Have you wrapped your app with
'NavigationContainer'?
This is my App.tsx:
import { createThemedStack } from '#my-custom-package'
//this is the reference to my custom navigator
const ThemedStack = createThemedStack()
function App() {
return (
<NavigationContainer>
<ThemedStack.Navigator>
<ThemedStack.Screen name='Screen 1' component={Screen1} />
<ThemedStack.Screen name='Screen 2' component={Screen2} />
...
</ThemedStack.Navigator>
</NavigationContainer>
)
}
export default App
ThemedStack.tsx - Here I want to apply some custom common styles to the stacks:
import * as React from 'react'
import { useNavigationBuilder, createNavigatorFactory, StackRouter } from '#react-navigation/native'
import { StackView } from '#react-navigation/stack'
// #ts-ignore
function ThemedStack({ initialRouteName, children, screenOptions, ...rest }) {
const { state, descriptors, navigation } = useNavigationBuilder(StackRouter, {
initialRouteName,
children,
screenOptions,
})
return <StackView {...rest} state={state} navigation={navigation} descriptors={descriptors} />
}
export default createNavigatorFactory(ThemedStack)
What am I doing wrong? I dont get it. The ThemedStack navigator is inside a NavigationContainer.
I did this based on https://reactnavigation.org/docs/custom-navigators/ RN Navigation docs, although they don't say anything about doing it in external package structure.
Thanks in advance :)
Sounds like you have #react-navigation/native in dependencies in your package which results in multiple versions installed. You should move it to peerDependencies.

Testing react-native-testing-library with react-navigation v5

I'm looking to test react-navigation v5 with react-native-testing-library. The documentation says to do the following.
import React from 'react';
import { NavigationContainer } from '#react-navigation/native';
import { render, fireEvent } from '#testing-library/react-native';
describe('Testing react navigation', () => {
test('page contains the header and 10 items', async () => {
const component = (
<NavigationContainer>
<AppNavigator />
</NavigationContainer>
);
const { findByText, findAllByText } = render(component);
const header = await findByText('List of numbers from 1 to 20');
const items = await findAllByText(/Item number/);
expect(header).toBeTruthy();
expect(items.length).toBe(10);
});
});
https://callstack.github.io/react-native-testing-library/docs/react-navigation
https://medium.com/#dariaruckaolszaska/testing-your-react-navigation-5-hooks-b8b8f745e5b6
This medium article suggests I create a MockedNavigator component. would this be a mockedNavigator component for all of my screens? Is this reusable?
import React from 'react';
import {NavigationContainer} from '#react-navigation/native';
import {createStackNavigator} from '#react-navigation/stack';
const Stack = createStackNavigator();
const MockedNavigator = ({component, params = {}}) => {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen
name="MockedScreen"
component={component}
initialParams={params}
/>
</Stack.Navigator>
</NavigationContainer>
);
};
export default MockedNavigator;
The answer I'm looking for is. Am I to use the real AppNavigator component within all my unit tests or am I to use a MockedAppNavigator?
Furthermore, how am I to pass props?
The documentation is unclear and I am looking for clarity. Many of the new components we are working with use hooks and the react-navigation-v5 is unable to access certain props.
There are two recommended approaches for RNTL tests involving React Navigation.
Either you test the same navigators you use in your app. This does not have to be the whole AppNavigator if your tests contain nested navigators. It can be only the one you want to test.
Or you test single screen not connected to any navigator. This would probably require refactor of that screen into the React Navigation-aware XxxScreen that would not be tested and XxxScreenContent that would receive relevant props from XxxScreen but would be indenpendend of XxxScreen.
You can find more details in the official React Navigation example in RNTL repo.
The mocked navigator approach is possible but not really advised as it's mocking too much, as such tests frequently have to rely on implementation details not visible to the users like route names, navigator nesting, etc.

React Navigation: "Cannot Add a child that doesn't have a YogaNode to a parent without a measure function" when using Custom Navigators

I'm trying to make the UI for my app in the below picture:
My App's UI
I follow the instruction of React Navigation to make the Custom Navigator according to the UI but it doesn't work in Android. The red screen appears with the message "Cannot Add a child that doesn't have a YogaNode to a parent without a measure function". Here is my code:
import React, { Component } from 'react';
import { createStackNavigator } from 'react-navigation';
import TabAboutScreen from './TabAbout';
import TabMyLessonScreen from './TabMyLesson';
import TabTeacherScreen from './TabTeacher';
import { ScrollView, View, Text } from '../../../components';
import TabNavigator from './TabNavigator';
import TopBar from './TopBar';
import styles from './styles';
import CourseHeader from './CourseHeader';
import theme from '../../../theme';
import i18n from '../../../i18n';
export const CourseDetailStackNavigator = createStackNavigator({
TabAbout: TabAboutScreen,
TabMyLesson: TabMyLessonScreen,
TabTeacher: TabTeacherScreen,
}, {
headerMode: 'none',
initialRouteName: 'TabAbout',
});
export default class TabCourseDetail extends Component {
static router = CourseDetailStackNavigator.router;
constructor(props) {
super(props);
this._handleOnBackButtonPress = this._handleOnBackButtonPress.bind(this);
}
_handleOnBackButtonPress() {
// do something
}
render() {
return (
<View style={styles.container}>
<TopBar textButton={i18n.t('CMBack')} title={i18n.t('CDCourseDetail')} onPress={this._handleOnBackButtonPress} />
<ScrollView
style={styles.scrollContainer}
stickyHeaderIndices={[1]}
showsVerticalScrollIndicator={false}
alwaysBounceVertical={false}
>
<CourseHeader />
<TabNavigator />
<View style={styles.test}>
<CourseDetailStackNavigator navigation={this.props.navigation} />
</View>
</ScrollView>
</View>
);
}
}
My evironment: react-navigation: 2.12.1, react-native: 0.55.4
I found out that the problem was that I put inside component by following the document of react navigation. It works well in iOS but doesn't work in Android.
Have you ever faced this problem. I'm looking forward to your solutions. Best regard.
Make sure you have not left any commented code in the return method and also have not left any text (string) without Text tag of react native.