How can I pass the navigator prop from react-native-navigation v2 to my splash screen via Navigation.setRoot - react-native

I am trying to migrate from react-native-navigation v1 to react-native-navigation v2. I am struggling to move from
Navigation.startSingleScreenApp
to
Navigation.setRoot
When I switch from Navigation.startSingleScreenApp (v1) to Navigation.setRoot (v2), I no longer have the navigator prop that I was relying on to navigate around the application.
I have copy and pasted all relevant code below
RegisterScreens
import { Navigation } from 'react-native-navigation';
import SplashScreenScreen from './components/SplashScreen';
import { Provider } from 'react-redux';
import React from "react";
import SCREEN from './screenNames';
export default function registerScreens(store) {
Navigation.registerComponent(
SCREEN.SPLASH_SCREEN,
() => props => (<Provider store={store}><SplashScreenScreen {...props} /></Provider>), () => SplashScreenScreen);
App
import { Platform } from 'react-native';
import { Navigation } from 'react-native-navigation';
import registerScreens from './registerScreens';
import { Colors, Fonts } from './themes';
import { store } from './configureStore';
import NavigationListener from './NavigationEventListener';
import configureNotification from './configureNotification';
import SCREEN from './screenNames';
import Reactotron from 'reactotron-react-native';
const navBarTranslucent = Platform.OS === 'ios';
configureNotification();
registerScreens(store);
new NavigationListener(store);
const STARTING_SCREEN = SCREEN.SPLASH_SCREEN;
Navigation.events().registerAppLaunchedListener(() => {
Reactotron.log('5');
Navigation.setRoot({
root: {
stack: {
children: [{
component: {
id: STARTING_SCREEN,
name: STARTING_SCREEN
}
}],
}
},
layout: {
orientation: 'portrait',
},
});
});
SplashScreen
import React from 'react';
import { View, StyleSheet, Text } from 'react-native';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import { PersistGate } from 'redux-persist/es/integration/react';
import { navigateToFirstScreen } from '../redux/splash';
import { Colors, Fonts, Metrics } from '../themes';
import { persistor } from '../configureStore';
export class SplashScreen extends React.Component {
navigateTo = (screen) =>
this.props.navigator.push({
screen,
overrideBackPress: true,
backButtonHidden: true,
animated: false,
navigatorStyle: {
disabledBackGesture: true,
},
});
render() {
const { dispatchNavigateToFirstScreen } = this.props;
return (
<PersistGate
persistor={persistor}
onBeforeLift={() => setTimeout(() => dispatchNavigateToFirstScreen(this.navigateTo), 2000)}><View style={styles.bodyContainer}
>
<Text>Jono</Text>
</View>
</PersistGate>
);
}
}
const styles = StyleSheet.create({
bodyContainer: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: Colors.splashScreen,
},
appTitleText: {
fontSize: Fonts.size.splashScreenTitle,
fontFamily: Fonts.type.extraBold,
lineHeight: Metrics.lineHeight.appTitle,
textAlign: 'center',
color: Colors.textLightColor,
},
});
SplashScreen.propTypes = {
navigator: PropTypes.shape({
push: PropTypes.func.isRequired,
}).isRequired,
dispatchNavigateToFirstScreen: PropTypes.func.isRequired,
};
const mapDispatchToProps = (dispatch) => {
return {
dispatchNavigateToFirstScreen: (navigateTo) =>
dispatch(navigateToFirstScreen(navigateTo)),
};
};
export default connect(null, mapDispatchToProps)(SplashScreen);

I spent multiple hours trying to solve this problem so I am going to post my conclusion as an answer.
this.props.navigator is not used anymore in 2.x.
You need to use Navigation
This dude had the same problem and reached the same conclusion: https://github.com/wix/react-native-navigation/issues/3795

Related

React Native props from App.js through react-navigation to Screen not working

i tried first steps with Theming in react-native with react-native-paper but the first step is not working.
AppViewContainer
export default compose(
lifecycle({
componentDidMount() {
if (Platform.OS === 'android') {
// eslint-disable-next-line no-unused-expressions
UIManager.setLayoutAnimationEnabledExperimental &&
UIManager.setLayoutAnimationEnabledExperimental(true);
}
},
}),
)(AppView);
AppView.js
export default function App() {
return(
<PaperProvider theme={theme}>
<Navigator onNavigationStateChange={() => { }} uriPrefix="/app" />
</PaperProvider>
)
}
Navigator
export default createAppContainer( createSwitchNavigator({
AuthLoading: LoadingScreen,
App: AppStack,
Tabs: TabNavigator,
Auth: AuthStack,
},
{
initialRouteName: 'AuthLoading',
}
));
in next step my HomeScreen is coming but there
this.props.theme //undefined
It seems the props not going through react navigation. How i can tell the Navigator to loop it through?
Many Thx
My Solution was now:
App.tsx - including redux
import React from 'react';
import { SafeAreaProvider } from 'react-native-safe-area-context';
import { AppearanceProvider } from 'react-native-appearance';
import { Provider } from 'react-redux';
import { Main } from './src/main';
import store from "./src/store"; //redux store
export default function App() {
return (
<Provider store={store}>
<SafeAreaProvider>
<AppearanceProvider>
<Main />
</AppearanceProvider>
</SafeAreaProvider>
</Provider>
);
}
Main.tsx
import React from 'react';
import {
Provider as PaperProvider,
DefaultTheme,
DarkTheme,
} from 'react-native-paper';
import { I18nManager } from 'react-native';
import * as Updates from 'expo-updates';
import { useColorScheme } from 'react-native-appearance';
import { PreferencesContext } from './context/preferencesContext';
import { RootNavigator } from './rootNavigator';
...
return (
<PreferencesContext.Provider value={preferences}>
<PaperProvider
theme={
theme === 'light'
? {
...DefaultTheme,
colors: { ...DefaultTheme.colors,
primary: 'red',
background: '#f8f8f8',
listbg: '#ffffff',
bg: require('../assets/splash.png')
},
}
: {
...DarkTheme,
colors: { ...DarkTheme.colors,
primary: '#1ba1f2',
listbg: 'rgba(255, 255, 255, 0.1)',
bg: require('../assets/splash.png')
},
}
}
>
<RootNavigator />
</PaperProvider>
</PreferencesContext.Provider>
);
};
now u can use 'theme' in every function component with global defined values and switch between dark and light mode.
const theme = useTheme(); // imported from react-native-paper
The complete example found on github here: https://github.com/Trancever/twitterClone
if this info was useful, vote up. thx.

How to write unit test cases for navigation stack in react native using enzyme jest?

I am new to react native and trying to write unit test cases. I am able to write snapshot test cases but not sure how to write unit test cases for navigation stack.
is there any way to write unit test cases for navigation?
navigator.js
import { createStackNavigator } from 'react-navigation-stack';
import { createAppContainer } from 'react-navigation';
import HomeScreen from '../component/HomeComponent/home';
import ThumbnailView from '../component/ThumbnailComponent/thumbnailView';
import AlbumDetailsView from '../component/AlbumDetailsComponent/albumDetailsView';
const MainNavigator = createStackNavigator({
HomeScreen: { screen: HomeScreen },
ThumbnailViewScreen: { screen: ThumbnailView },
AlbumDetailsViewScreen: { screen: AlbumDetailsView },
},
{
defaultNavigationOptions: {
headerTintColor: '#fff',
headerStyle: {
backgroundColor: '#0c82f3',
},
headerTitleStyle: {
fontWeight: 'bold',
},
},
});
const NavigationApp = createAppContainer(MainNavigator);
export default NavigationApp;
You can test navigate function in your components like this:
import HomeScreen from '../component/HomeComponent/home';
import { shallow, ShallowWrapper } from "enzyme";
import React from "react";
import { View } from "react-native";
const createTestProps = (props) => ({
navigation: {
navigate: jest.fn()
},
...props
});
describe("HomeScreen", () => {
describe("rendering", () => {
let wrapper;
let props;
beforeEach(() => {
props = createTestProps({});
wrapper = shallow(<HomeScreen {...props} />);
});
it("should render a <View /> and go to ThumbnailViewScreen", () => {
expect(wrapper.find(View)).toHaveLength(1); // Some other tests
expect(props.navigation.navigate).toHaveBeenCalledWith('ThumbnailViewScreen'); // What you want
});
});
});
HomeScreen.js :
import React, { Component } from "react";
import { Text, View } from "react-native";
export class HomeScreen extends Component {
componentDidMount = () => {
this.props.navigation.navigate("ThumbnailViewScreen");
};
render() {
return (
<View>
<Text>This is the HomeScreen.</Text>
</View>
);
}
}
export default HomeScreen;
You can find more details here

react native updating redux store from app.js not working

https://snack.expo.io/#mparvez19861/redux-example
I have below code in app.js in
import React, { Component } from 'react';
import { Platform, StyleSheet, Text, View } from 'react-native';
import Geolocation from 'react-native-geolocation-service';
import firebase from './FireBaseSetup/Firebase'
// import DrawerNavigatorExample from './Navigations';
import Navigator from './Navigator'
import Permissions from 'react-native-permissions'
import { PermissionsAndroid } from 'react-native';
import PermissionsList from './Utitliy/PermissionsList'
import Contacts from 'react-native-contacts';
import { Provider, connect } from 'react-redux';
import { store, persistor, setCurrentLocation } from './redux/app-redux';
import { PersistGate } from 'redux-persist/integration/react'
import SplashScreen from './screens/SplashScreen'
const mapDispatchToProps = (dispatch) => {
return {
setCurrentLocation: (location) => { dispatch(setCurrentLocation(location)) }
};
}
const ConnectedApp = connect(mapDispatchToProps)(Navigator);
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
latitude: null,
longitude: null,
error: null,
};
}
async componentDidMount() {
this.getContacts();
await new PermissionsList().requestLocationPermission();
Geolocation.getCurrentPosition(
(position) => {
// this.setState({
// latitude: position.coords.latitude,
// longitude: position.coords.longitude,
// error: null,
// });
this.props.setCurrentLocation(position);
firebase.firestore().collection('locations').doc('8686').set({
locations: position
})
},
(error) => alert(JSON.stringify(error)),
{ enableHighAccuracy: true, timeout: 20000, maximumAge: 1000, distanceFilter: 100 }
);
this.watchId = Geolocation.watchPosition(
(position) => {
// this.setState({
// latitude: position.coords.latitude,
// longitude: position.coords.longitude,
// error: null,
// });
this.props.setCurrentLocation(position);
firebase.firestore().collection('locations').doc('8686').set({
locations: position
})
},
(error) => this.setState({ error: error.message }),
{ enableHighAccuracy: false, timeout: 20000, maximumAge: 10000, distanceFilter: 1 },
);
}
componentWillUnmount() {
Geolocation.clearWatch(this.watchId);
}
render() {
return (
<Provider store={store}>
<PersistGate loading={null} persistor={persistor}>
<View style={styles.container}>
<ConnectedApp />
</View>
</PersistGate>
</Provider>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
},
});
Navigator.js
import React, { Component } from 'react';
import { View, Text, TouchableHighlight, Image } from 'react-native';
import { createDrawerNavigator, createStackNavigator , createSwitchNavigator, createAppContainer } from 'react-navigation';
import {
ActivityIndicator,
AsyncStorage,
Button,
StatusBar,
StyleSheet
} from 'react-native';
// import firebase from 'react-native-firebase';
// import { store } from '../redux/app-redux';
import Screen2 from './screens/Screen2';
import SplashScreen from './screens/SplashScreen'
import Login from './screens/Login'
import SignOutScreen from './screens/SignOutScreen'
import screendesign from './screens/screendesign'
import EmailPwdLogin from './screens/EmailPwdLogin'
import friends from './screens/friends'
import LoginScreen from './screens/LoginScreen';
import SignupScreen from './screens/SignupScreen';
const AuthStack = createStackNavigator({
// { SignIn: SignInScreen }
// SignIn: { screen: EmailPwdLogin }
Login: { screen: Login },
Signup: { screen: SignupScreen },
});
const drNav = createDrawerNavigator(
{
friends: {
screen: friends
},
Screen2: {
screen: Screen2
},
SignOut: {
screen: SignOutScreen
}
}
)
export default createAppContainer(createSwitchNavigator(
{
// screendesign: screendesign,
SplashScreen: SplashScreen,
App: drNav,
AuthStack: AuthStack
},
{
initialRouteName: 'SplashScreen',
}
));
Redux file
const setCurrentLocation = (location) => {
alert(JSON.stringify(location))
return {
type: "setCurrentLocation",
value: location
};
};
export { setCurrentLocation };
But this setCurrentLocation is not being fired from app.js
please help.
Thanks
You are trying to dispatch a redux action from App.js which is the entry point of your application and where you initialise your redux store. You can use this.props.setCurrentLocation(position) from any connected component within the Provider component, but App.js is outside of it.
So you need to call it like so:
store.dispatch(setCurrentLocation(position))
I tried to run your snack to see if you have any other issues but it throws an error.
Hope this helps
You forget to connect mapDispatchToProps:
export default connect(mapStateToProps,mapDispatchToProps)(App);

How to navigate to another screen from a Redux form with React navigation?

Basically I want that within a props.handleSubmit function you can navigate to another screen.
Here is my code :
import React, { Component } from "react";
import { createStackNavigator, createAppContainer } from 'react-navigation';
import { Provider } from 'react-redux';
import Store from './Store/Store'
import ScreenSignUp from './Navigation/ScreenSignUp';
import ScreenSignIn from './Navigation/ScreenSignIn';
import ScreenHome from "./Navigation/ScreenHome";
class App extends Component {
render() {
return (
<Provider store={Store}>
<AppContainer />
</Provider>
);
}
}
export default App;
const RootStack = createStackNavigator(
{
Home: ScreenHome,
SignUp: ScreenSignUp,
SignIn: ScreenSignIn,
},
{
initialRouteName: 'SignIn',
defaultNavigationOptions: {
header: null,
},
},
);
const AppContainer = createAppContainer(RootStack);
you can do it like this
export default reduxForm({
form: 'SignInForm',
onSubmitSuccess : (result, dispatch, props) => {
props.navigation.navigate('Register');
},
onSubmitFail : () => {
Toast.show({
text: "Enter Passport Number",
duration: 2500,
position: "top",
textStyle: { textAlign: "center" }
});
}
validate,
})(SignInForm);

Redux error in react-native

I'm trying to implement redux to show balance in multiple screens as I update balance in single screen it should reflect in all other screens/components.
I'm pretty new to redux. As you know with complexity around redux, its even making difficult to implement it.
I followed some examples in GitHub and youtube and started implementing it .
Under Actions folder I have. following two files
counteractions.js
import * as types from './actionTypes.js';
//ActionCreator methods
export function updateBalance(balanceInfo) {
return {
type: types.LEDGER_BALANCE,
payLoad: { balanceInfo }
}
}
Under Reducers folder.I have this file
balance.js
import * as types from '../actions/actionTypes.js';
const initialState = {
balance: 0
}
// reducer functions .. accepts current/initial state , actions and returns new state
const balanceReducer=(state,action)=>
{
switch (action.type) {
case types.LEDGER_BALANCE:
return {
balance: action.payload.balanceInfo
}
break;
default:
break;
}
}
export default balanceReducer;
in ConfigureStore.js
import {createStore} from 'redux';
import rootReducer from './reducers/index.js';
import balanceReducer from './reducers/balance.js';
const initailState = {
balance: 0,
}
export const store=createStore(balanceReducer,balanceReducer);
App.js
/**
* Sample React Native App
* https://github.com/facebook/react-native
* #flow
*/
import React, { Component } from 'react';
import { Provider } from 'react-redux';
//Provider - makes redux store available to connect() class in component hierarchy below
import { applyMiddleware, createStore, compose, combineReducers } from "redux";
import thunkMiddleware from 'redux-thunk';
import createLogger from 'redux-logger';
import rootReducer from './reducers/index.js';
//import store from './configureStore.js';
import {
Platform,
StyleSheet,
Text,
View,
TouchableOpacity,
TextInput
} from 'react-native';
import ReduxDemo from "./reduxDemo.js";
import { store, reducer } from './balanceDemo.js';
const instructions = Platform.select({
ios: 'Press Cmd+R to reload,\n' +
'Cmd+D or shake for dev menu',
android: 'Double tap R on your keyboard to reload,\n' +
'Shake or press menu button for dev menu',
});
export default class App extends Component<{}> {
constructor(props) {
super(props);
this.state = {
balancelocal: '',
}
}
_updateLedger = () => {
// store.dispatch({ type: 'BALANCE', payLoad: '500' });
store.dispatch({ type: 'BALANCE', payLoad: 'Your balance is 8000 MUR' });
}
render() {
store.subscribe(() => {
this.setState({
balancelocal: store.getState(),
})
//this.balanceInfo = store.getState().balance;
// alert(this.state.balancelocal);
});
return (
<View style={styles.container}>
<TouchableOpacity onPress={this._updateLedger}>
<Text>Update balance</Text>
</TouchableOpacity>
<TextInput style={{height:100,width:400}} value={this.state.balancelocal}/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
instructions: {
textAlign: 'center',
color: '#333333',
marginBottom: 5,
},
});
I'm yet to complete configure store file. and. I'm wondering. where I have to subscribe and dispatch actions ..
I want to update balance with button click from app.js
I have. to update balance in another page automatically..
Please guide me to understand and implement redux .Please suggest better folder structure and better way to implement redux.
There is quite a bit here to understand.
The basic workflow is (you can have the receiving component as a different one)
Component Button > Action > Reducer > Component Props > Render
To achieve this you need both the setup of the store and the invoking of the "event" through redux.
Below is an example (excuse if not perfect, just typed into here), but the way the other component gets the value from the action is becuase it uses the 'connect' HOC. Everytime redux gets a state change it calls all components that are 'connected'. Here we take the updated balance and return it as part of the 'mapStateToProps' function, which is just setting the components props with that object. The balance is then accessed via this.props.balance and displayed.
This becomes more useful if you want to either call an api in the action and store the result in the reducer. All connected components will then get that new data.
Note1: I have only used redux-thunk middleware to dispatch so forgive me for using that.
Note2: This is a simple example. When the app gets better you will need to prevent over-rendering since any reducer changes will invoke all connected components. I use reselect here.
Setup
reducers.js
import { combineReducers } from 'redux';
import { balanceReducer } from 'balanceReducer';
export default combineReducers({
balanceReducer
})
store.js
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk'
import combineReducers from './reducers'
export default function configureStore() {
let store = createStore(combineReducers, applyMiddleware(thunk));
return store;
}
index.js
import React, { Component } from 'react';
import { AppRegistry, View } from 'react-native';
import { Provider } from 'react-redux';
import configureStore from './store';
import Component1 from './component1';
const store = configureStore()
const myapp = () => (
<Provider store={store}>
<View>
<Component1 />
<View>
</Provider>
)
AppRegistry.registerComponent('myapp', () => myapp);
Components
component1.js (key part is the connect HOC)
import { connect } from 'react-redux';
import React, { Component } from 'react';
import { Text, View, TouchableOpacity } from 'react-native';
import { updateBalance } from './action';
class Component1 extends Component {
_updateLedger = () => {
this.props.updateBalance(500);
}
render() {
const { balance } = this.props;
return (
<View>
<TouchableOpacity onPress={this._updateLedger}>
<Text>Update balance</Text>
</TouchableOpacity>
<Text>{balance}</Text>
</View>
)
}
}
function mapStateToProps(state) {
return {
balance: state.balanceReducer.balance
}
}
function mapDispatchToProps(dispatch) {
return {
updateBalance = (balanceInfo) => dispatch(updateBalance(balanceInfo))
};
}
export default connect(
mapStatetoProps,
mapDispatchToProps
)(Component1)
action.js
export function updateBalance(balanceInfo) {
return {
type: types.LEDGER_BALANCE,
payLoad: { balanceInfo }
}
}
balanceReducer.js (key part here is to return new state)
const initialState = {
balance: 0,
}
export function balanceReducer(state = initialState, action) {
if(action.type === types.LEDGER_BALANCE) {
return {
balance: action.payLoad.balanceInfo
}
}
}