React Native - Accessing drawer navigation outside of AppNavigator - javascript

App.js
<Store>
<Navbar />
<AppNavigator ref={navigatorRef => {
NavigationService.setTopLevelNavigator(navigatorRef);
}} />
</Store>
I wanna be able to access
props.navigation.openDrawer();
from navbar but I get
undefined is not an object (evaluating 'props.navigation.openDrawer')
onPress
Navbar.js:70:29
etc..
How can I allow NavBar to access the drawer?

I suppose you are following Navigating without the navigation prop (if you don't then you should in your case). Then in NavigationService.js add openDrawer method
// NavigationService.js
import { DrawerActions } from 'react-navigation-drawer';
...
// add this function
function openDrawer(routeName, params) {
_navigator.dispatch(DrawerActions.openDrawer());
}
export default {
...
// and export it
openDrawer
};
then instead of props.navigation.openDrawer(), use
NavigationService.openDrawer()
don't forget to make respective imports

this is how i used openDrawer without navigation prop:
in your App.js (or other router)
1/
import { DrawerActions } from '#react-navigation/native';
2/
export const navigationRef = React.createRef();
3/
export function openDrawer(routeName, params) {
navigationRef.current.dispatch(DrawerActions.openDrawer());
}
4/ inside your navigation container add this ref
<NavigationContainer ref={navigationRef}>
5/ call your function somewhere where openDrawer is created:
<TouchableOpacity onPress={() => openDrawer()}>
<Image
source={tab}
style={{
width: 20,
height: 20,
}}
/>
</TouchableOpacity>

Related

React Native: calling an existing function in a react-native-webview using injectJavascript

The following is my use-case:
There is an existing web app (say https://example.com). It has some functions in modules that are called on load of the page. For example:
// index.js
import * as test from "./test.js";
The test.js has its functions
// test.js
const check = () => {
alert("check called");
}
export { check }
Now from the React Native side I wish to call the check function using injectJavascript. The App.js looks like this:
import React, { Component } from 'react';
import { View, Text, Button } from 'react-native';
import { WebView } from 'react-native-webview';
class App extends Component {
constructor(props) {
super(props)
this.webviewRef = null;
}
render() {
return (
<>
<View style={{ flex: 1, alignContent: 'center', justifyContent: 'center' }}>
<Button
title="Check"
onPress={() => {
const clientResponseCode = `
test.check();
true;
`;
this.webviewRef.injectJavaScript(clientResponseCode);
}}
/>
</View>
<WebView
source={{ uri: 'https://example.com' }}
ref={ref => this.webviewRef = ref}
javaScriptEnabled={true}
onMessage={this.onMessage}
/>
</>
);
}
}
//make this component available to the app
export default App;
What works inside clientResponseCode:
document.querySelector("#any-id").innerHTML = "....";
What does not work inside clientResponseCode:
test.check();
document.test.check();
window.test.check();
I also tried to to use the postMessage method with addEventListener on the web side but it does not get invoked. I tried both these below options.
window.postMessage("hello");
windows.ReactNativeWebView.postMessage("hello");
What will be the way to invoke the check method from the test module that is imported in index.js on the website?
Or is it that only the DOM is available to App.js and only dynamically loading of javascript code is permitted? If so, then I will create some webcomponent to inject the dynamic passing of objects (blob, in my case) to the DOM.
Thanks for the help
The following seemed to have worked.
In index.js on the web side
//index.js
..
..
// add the following
window.check = test.check; // without brackets
...
on the React App side
// App.js
...
...
const clientResponseCode = `
window.check();
true;
`;
this.webviewRef.injectJavaScript(clientResponseCode);
It is possible to pass parameters too to the check function

How do I access react-navigation from inside a component that isn't the HomeScreen?

I'm building a React Native app. I have imported createStackNavigator from react-navigation. I'm able to get it working on my Home screen - I click a button, it brings me to a new component. This is the code that I'm using to bring it into my Home.js
// src/components/Home/Home
export class Home extends Component {
render() {
return (
<React.Fragment>
<Button
title="Test button"
onPress={() => this.props.navigation.navigate('Roads')}
/>
<StatusBar />
<Header />
<Menu />
</React.Fragment>
);
}
}
const RootStack = createStackNavigator(
{
Home: Home,
Roads: Roads,
},
{
initialRouteName: 'Home',
}
);
export default class App extends React.Component {
render() {
return <RootStack />;
}
}
My Home page takes in a Menu which has a list of MenuItems. I am trying to get the MenuItems to jump to the appropriate pages. When I try to bring in the navigation inside MenuItem.js's render method, like so:
// src/components/Roads/Roads
render() {
const { navigate } = this.props.navigation;
console.log(this.props, "props is here");
I get the following error message:
TypeError: undefined is not an object (evaluating 'this.props.navigation.navigate').
Do I need to pass the navigator down in props to Menu.js and then to MenuItem.js? The docs give examples but it seems to be examples that assume you jam all your code into one file rather than across several components.
Have I set this up correctly?
When using a Navigator from react-navigation only the components you declare as Screens inherit the navigation prop (in your case Home and Roads)
This means that you will need to pass it as a prop to its children as you said:
<Menu navigation={this.props.navigation} />
<MenuItem navigation={this.props.navigation} />
In case anyone is wondering how to navigate from a component that isn't inside a Navigator then I suggest reading this part of the react-navigation documentation
https://reactnavigation.org/docs/en/navigating-without-navigation-prop.html

Error: this.props.header is not a function when using StackNavigator

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')

react native - On button press load the whole screen with parent component

I am trying to load my parent component from child component on button press. But it's not rendering the parent components from btnPress method. I am not getting any error.
onButtonPress
<Button onPress={() => btnPress(parent_id, id)}>
<Icon name="arrow-forward" />
</Button>
btnPress Function
function btnPress(parent_id, id) {
const App = () => (
//I have tried this way but this didn't work. No any error, i can see log on console
<Container>
<Headerc headerText={'Fitness sdaf'} />
<ExerciseList pId={parent_id} mId={id} />
</Container>
);
console.log(id);
AppRegistry.registerComponent('weightTraining', () => App);
}
full code(child component)
import React from 'react';
import { Right, Body, Thumbnail, Container, ListItem, Text, Icon } from 'native-base';
import { AppRegistry
} from 'react-native';
import Headerc from './headerc';
import ExerciseList from './exerciseList';
import Button from './Button';
const ExerciseDetail = ({ exercise }) => {
const { menu_name, menu_icon, parent_id, id } = exercise;
function NumberDescriber() {
let description;
if (menu_icon === 'noimg.jpg') {
description = `http://www.xxxxxx.com/uploads/icons/${menu_icon}`;
} else if (menu_icon === 'noimg.jpg') {
description = menu_icon;
} else {
description = `http://www.xxxxx.com/uploads/icons/${menu_icon}`;
}
return description;
}
function btnPress(parent_id, id) {
const App = () => (
<Container>
<Headerc headerText={'Fitness sdaf'} />
<ExerciseList pId={parent_id} mId={id} />
</Container>
);
console.log('-------------------------------');
console.log(id);
console.log('+++++++++++++++++++++++++++');
AppRegistry.registerComponent('weightTraining', () => App);
}
return (
<ListItem>
<Thumbnail square size={80} source={{ uri: NumberDescriber() }} />
<Body>
<Text>{menu_name}</Text>
<Text note> {menu_name} exercise lists</Text>
</Body>
<Right>
<Button onPress={() => btnPress(parent_id, id)}>
<Icon name="arrow-forward" />
</Button>
</Right>
</ListItem>
);
};
export default ExerciseDetail;
Please do let me know, if you need more information.
I would not suggest doing that way, it look totally anti-pattern and not.
better try with navigation or create a pattern like this
inside your index.js or index.android.js or index.ios.js
import App from './App' //your app level file
AppRegistry.registerComponent('weightTraining', () => App);
now in your app js file
export default App class extends React.Component{
constructor(props){
super(props);
this.state ={
component1:false,
component2:true,
}
}
btnPressed =()=>{
//handle state update logic here
}
render(){
if(this.state.component1) return <Component1/>
return <Component2/>
}
}
**** not the best solution available, play around and you will get best
To navigate from this component to your parent component unless you want to implement your own navigation which isn't recommended, you should look into one that's already built and adopted by many in the react-native ecosystem.
Some of the biggest ones:
React Native Navigation
React Navigation
React Native Router
I personally highly recommend option number 1, since it seems to be the most production tested and production ready implementation out there

Route should declare a screen. [ React Native Navigation Error]

Hi I am new to react native and I am facing strange issue with routing. I am doing something wrong but need someone to guide me.
index.android.js
import { LandingScreen } from './src/components/landing_screen.js'
import HomeScreen from './src/app_component.js'
import { StackNavigator } from 'react-navigation';
const SimpleApp = StackNavigator({
Home: { screen: HomeScreen },
Landing: { screen: LandingScreen},
});
AppRegistry.registerComponent('HomeScreen', () => SimpleApp);
app_component.js
// Other imports ...
export default class HomeScreen extends Component {
static navigationOptions = {
title: 'Home Screen',
};
render() {
const { navigate } = this.props.navigation;
return (
<View style={styles.container}>
<Text style={styles.welcome}>
Welcome to React Native!
</Text>
<Text style={styles.instructions}> Hello CHannoo!!!</Text>
<Text style={styles.instructions}>
To get started, edit index.android.js
</Text>
<Text style={styles.instructions}>
Double tap R on your keyboard to reload,{'\n'}
Shake or press menu button for dev menu
</Text>
<Button
title="Go to 2nd Page"
onPress={() =>
// alert('hello');
navigate('LandingScreen')
// navigate('Home', { name: 'Jane' })
}
/>
</View>
);
}
componentDidMount () {
SplashScreen.close({
animationType: SplashScreen.animationType.scale,
duration: 850,
delay: 500,
})
}
}
landing_screen.js
export default class LandingScreen extends Component {
static navigationOptions = {
title: 'Landing Screen Title',
};
render() {
return (........)
}
It works fine if we remove route Landing. But when we add this route we get error.
Route 'Landing' should declare a screen. For example ......
Your LandingScreen has been exported as default but you imported it by name.
your import statement is like this:
import { LandingScreen } from './src/components/landing_screen.js'
replace it with line below (without curly brackets):
import LandingScreen from './src/components/landing_screen.js'
it should solve the problem.
BUT you will probably get a new error as #Medet pointed out because you have to change this line:
navigate('LandingScreen')
to:
navigate('Landing')
since your screen name is Landing.
You calling navigate('LandingScreen')
But screen name is Landing
+ #Dusk's answer should solve

Categories

Resources