React Navigation + Redux: How to pass props into StackNavigator? - react-native

I'm trying to set up my StackNavigator with redux.
import { connect } from "react-redux";
import { StackNavigator } from "react-navigation";
import ChatList from "../chat/chat-list";
import ChatDetail from "../chat/chat-detail";
// how do we pass props into this??
const ChatContainer = StackNavigator({
ChatList: {
screen: ChatList
},
ChatDetail: {
screen: ChatDetail,
navigationOptions: ({ navigation }) => ({
title: "Jesus",
tabBarVisible: false
})
}
});
export default connect(state => ({
cool: state.cool,
}), dispatch => ({}))(ChatContainer);
how would I pass cool into StackNavigator and down to ChatList?

you can use Navigator Props
const SomeStack = StackNavigator({
// config
});
<SomeStack
screenProps={/* this prop will get passed to the screen components as this.props.screenProps */}
/>
https://reactnavigation.org/docs/navigators/stack#Navigator-Props
new link for react-navigation 3.x
https://reactnavigation.org/docs/en/stack-navigator.html#navigator-props

Related

Passing navigation to BottomNavigation

In my React Native app, I use react-navigation version 5.
How do I pass the navigation object to the scenes in BottomNavigation?
Here's the component where I create the BottomNavigation:
import React from 'react';
import { BottomNavigation } from 'react-native-paper';
// Components
import CodeScanner from '../../../screens/vendors/CodeScannerScreen';
import Home from '../../../screens/home/HomeScreen';
import Vendors from '../vendors/VendorsStack';
// Routes
const homeRoute = () => <Home />;
const vendorsRoute = () => <Vendors />;
const scanRoute = () => <CodeScanner />;
const HomeTabs = (props) => {
const [index, setIndex] = React.useState(0);
const [routes] = React.useState([
{ key: 'home', title: 'Home', icon: 'newspaper-variant-multiple' },
{ key: 'vendors', title: 'Vendors', icon: 'storefront' },
{ key: 'codescanner', title: 'Scan', icon: 'qrcode-scan' }
]);
const renderScene = BottomNavigation.SceneMap({
home: homeRoute,
vendors: vendorsRoute,
codescanner: scanRoute
});
return (
<BottomNavigation
navigationState={{ index, routes }}
onIndexChange={setIndex}
renderScene={renderScene}
labeled={false} />
);
}
export default HomeTabs;
In this code, I do have the navigation in the props but haven't been able to figure out a way to pass navigation to the screens. Please also note that the vendor one is actually a stack navigator. In particular, that's where I need access to navigation so that I can open up other screens.
You should pass it from tabBar prop like below (I use TypeScript) and BottomTabBarProps:
export declare type BottomTabBarProps = BottomTabBarOptions & {
state: TabNavigationState
descriptors: BottomTabDescriptorMap
navigation: NavigationHelpers<ParamListBase, BottomTabNavigationEventMap>
}
So you pass BottomTabBarProps to your custom tab component
<BottomTab.Navigator
screenOptions={TabBarVisibleOnRootScreenOptions}
initialRouteName={'Explore'}
tabBar={(props: BottomTabBarProps) => <HomeTabs {...props} />}
>
<BottomTab.Screen name="Explore" component={ExploreScreen} />
...
</BottomTab.Navigator>
So inner HomeTabs you got props.navigation
I think you don't need to pass navigation to BottomNavigation. Because in react-navigation v5 navigation can access anywhere with hook
Read this document https://reactnavigation.org/docs/connecting-navigation-prop/
You may pass data to the vendors screen through the route like so:
const HomeTabs = ( props ) => {
...
const [routes] = React.useState([
...
{ key: 'vendors', title: 'Vendors', icon: 'storefront', props: props },
...
]);
...
}
On your vendor screen you can then retrieve and use the data like this:
const VendorsRoute = ({ route }) => {
const props = route.props;
<Vendors />
}

Attempts to access this ref will fail. Did you mean to use React.forwardRef()? with jest

The warning doesn't happen during debug or release mode builds but when testing App.js:
App.js
import React from 'react';
import { View, AppRegistry } from 'react-native';
import { gestureHandlerRootHOC } from 'react-native-gesture-handler';
import { LocalizationProvider } from 'library/localization';
import NavigationService from 'library/NavigationService';
import AppNavigator from 'library/AppNavigator';
const App = () => {
return (
<>
<LocalizationProvider>
<AppNavigator
ref={(navigatorRef) => {
NavigationService.setTopLevelNavigator(navigatorRef);
}}
/>
</LocalizationProvider>
</>
);
};
AppRegistry.registerComponent(appName, () => gestureHandlerRootHOC(App));
export default App;
the error doesn't appear if I remove the ref from AppNavigator.
AppNavigator.js
const guestUserNavigation = createStackNavigator(
{
Login: {
screen: LoginScreen
},
Logout: {
screen: LogoutActivity
}
},
{
headerMode: 'none',
initialRouteName: 'Login'
}
);
const userNavigation = createStackNavigator(
{
Home: {
screen: HomeScreen
}
},
{
headerMode: 'none',
initialRouteName: 'Home'
}
);
const App = createSwitchNavigator({
loader: {
screen: AuthLoadingScreen
},
Auth: {
screen: guestUserNavigation
},
App: {
screen: userNavigation
}
});
const AppNavigator = createAppContainer(App, {
initialRouteName: 'loader'
});
export default AppNavigator;
App.test.js
import React from 'react';
import { render } from 'react-native-testing-library';
import App from './App';
describe('App', () => {
it('should render the App', () => {
const result = render(<App />).toJSON();
expect(result).toMatchSnapshot();
});
});
mocks/react-navigation.js
jest.mock('react-navigation', () => ({
createAppContainer: jest
.fn()
.mockReturnValue(function NavigationContainer(props) {
return null;
}),
createSwitchNavigator: jest.fn(),
NavigationActions: {
navigate: jest.fn().mockImplementation((x) => x)
}
}));
now the test passes with the following warning that is bugging me and I cant seem to get it to work:
Warning: Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?
Check the render method of `App`.
in NavigationContainer (at App.js:29)
in LocalizationProvider (at App.js:28)
in App (at App.test.js:7)
using forwardRef works but then the app doesn't render.
AppNavigator shouldn't return null because this makes the mock useless to test App.
In order to receive a ref, AppNavigator should be class or forwardRef component. createAppContainer arguments can be optionally represented in element hierarchy in order to be testable by a snapshot:
...
createAppContainer: jest.fn((Comp, options) => React.forwardRef((props, ref) => {
return <div data-testid="AppContainer" ref={ref}>
<div data-testid="createAppContainer component">
{Comp.name}
</div>
<div data-testid="createAppContainer options">
{JSON.stringify(options)}
</div>
{children}
</div>;
})),
...

Import a created redux store to dispatch an action from a non connected component

I am using react-native and redux. I am also using react navigation, I have a bottom tab navigator where the second RouteConfig navigationOptions provides a function for tabBarOnPress. I want to dispatch some action to redux inside this callback.
import store from "./Store"
const BottomTab = createBottomTabNavigator(
{
First: FirstScreen,
Second: {
screen: SecondScreen,
navigationOptions: {
tabBarOnPress: ({ navigation }: any) => {
store.dispatch(someAction())
navigation.navigate("SecondScreenModal");
}
}
},
Third: ThirdScreen
},
{
navigationOptions: ({ navigation }) => ({
//..
}
);
As I can't find a way to connect the bottom tab navigator, I would like to know if it is bad practice in this case to directly import the created redux store (the same one I pass as a prop to the provider).
import rootReducer from "./RootReducer";
import {createStore} from "redux";
store = createStore(rootReducer)
export default store
Inside the screen that you wish to dispatch the redux action (in your case SecondScreen), set a navigation param equal to the function you want to execute. You can name the parameter anything, but I usually name it according to how I will use it.
componentDidMount = () => {
this.props.navigation.setParams({ tabBarOnPress: this.props.myReduxActionCreator })
}
Then inside your screens navigation options, you can do something like this to access the function. Make sure its the same parameter name from above, so in this exmaple, 'tabBarOnPress'
const BottomTab = createBottomTabNavigator(
{
First: FirstScreen,
Second: {
screen: SecondScreen,
navigationOptions: {
// Access the function inside navigation params here
tabBarOnPress: ({navigation}) => navigation.getParam('tabBarOnPress', null)
}
},
Third: ThirdScreen
},
{
navigationOptions: ({navigation}) => ({})
}
)
After const BottomTab = createBottomTabNavigator() this complete code crate mapStateToProps, mapDispatchToProps arrow functions.
export default connect(mapStateToProps, mapDispatchToProps)(BottomTab);
i believe you want to do like this.

React Navigation: Is it possible to goBack() or pop() with params?

I'm using React Navigation (V2) and I have a basic set up of screens like this:
import {
createDrawerNavigator,
createStackNavigator,
createSwitchNavigator
} from "react-navigation";
import DetailScreen from "../screens/DetailScreen";
import HomeScreen from "../screens/HomeScreen";
import LoginScreen from "../screens/LoginScreen";
import SettingsScreen from "../screens/SettingsScreen";
const StackNavigator = createStackNavigator({
Home: { screen: HomeScreen },
Detail: { screen: DetailScreen }
});
StackNavigator.navigationOptions = ({ navigation }) => {
let drawerLockMode = "unlocked";
if (navigation.state.index > 0) {
drawerLockMode = "locked-closed";
}
return {
drawerLockMode
};
};
const DrawerNavigator = createDrawerNavigator({
HomeStack: { screen: StackNavigator },
Settings: { screen: SettingsScreen }
});
const Navigator = createSwitchNavigator(
{ LoginScreen, DrawerNavigator },
{ initialRouteName: "LoginScreen" }
);
export default Navigator;
While using my app the user ends up on the DetailScreen, makes some choices and is then supposed to go back to the HomeScreen. I want to pass params to the HomeScreen while going back. Unfortunately it seems like pop() and goBack() do not accept any params.
How can I go back a screen and pass params while doing so?
You can use the listeners for the screen and fire your refresh changes there.
didFocus - the screen focused (if there was a transition, the transition
completed)
// In your component
componentDidMount () {
this._onFocusListener = this.props.navigation.addListener('didFocus', (payload) => {
// Update the component (API calls here)
});
}

Navigate when clicking on a button in react native

I could successfully implemented the stack and tab navigation in my project.
import React from "react";
import { StackNavigator } from "react-navigation";
import { TabNavFooter } from "./TabNavFooter";
import { SIGNIN_KEY, SIGNUP_KEY } from "../config/routeKeys";
import {
SignupScreen,
SigninScreen,
MainFeedScreen,
ProfilePageScreen,
CommentScreen
} from "../screens";
export const Routes = StackNavigator({
signin: { screen: SigninScreen },
comments: { screen: CommentScreen },
mainfeed: { screen: TabNavFooter },
signup: { screen: SignupScreen },
profilePage: { screen: ProfilePageScreen }
});
Now I want to navigate when I click on the comment button. My routes are in router/index.js file. How can I use this to navigate when I'm on another component? I tried this, but it didn't work.
export default class Post extends Component {
constructor(props) {
super(props);
}
commentPressedHandler = () => {
this.props.navigation('comments');
};
you should add navigate like this this.props.navigation.navigate('Your Screen')
so try to change your code like :
commentPressedHandler = () => {
this.props.navigation.navigate('comments');
};
instead of :
commentPressedHandler = () => {
this.props.navigation('comments');
};
Here is a solution using #react-navigation/native and #react-navigation/native-stack from the react native navigation documentation.
https://reactnative.dev/docs/navigation
Build React-Native route controller
import * as React from 'react';
import { NavigationContainer } from '#react-navigation/native';
import { createNativeStackNavigator } from '#react-navigation/native-stack';
import Login from "./Login";
import Home from "./Home";
const Stack = createNativeStackNavigator();
const NavigationStack = () => {
return (
<NavigationContainer>
<Stack.Navigator screenOptions={{headerShown: false}}>
<Stack.Screen
name="Login"
component={Login}
/>
<Stack.Screen name="Home" component={CreateAccount} />
</Stack.Navigator>
</NavigationContainer>
);
};
export default NavigationStack;
Get the navigation component injected into your view ex:
const Login = ({ navigation }) => {
Implement a function for your button that navigates to a different view.
Login = async event => {
console.log("Login")
// Do Login Stuff
// Navigate to Home View by using navigation component
navigation.navigate("Home");
}