react-native: react-navigation drawer labels - reactjs

I want to have drawer labels/separators in the drawer navigator.
Somewhat like this
How would I do this?

Easy. What you are looking at is called contentComponent. Basically you will need to inject a contentComponent prop into your Drawer navigator.
contentComponent Component used to render the content of the drawer, for example, navigation items. Receives the navigation prop for the drawer. Read more here
import DrawerContent from '../app/components/DrawerContent'
const drawerConfiguration = {
initialRouteName: 'MainStackNavigatior',
headerMode: 'screen',
drawerWidth: deviceWidth / 1.38,
contentComponent: DrawerContent
}
Where contentComponent is just a ScrollView containing a list of customisable items.
class DrawerContent extends Component {
onItemPress(item) {
const { navigation } = this.props;
navigation.navigate(item.key);
}
renderDrawerItem(route) {
const { drawerItems } = this.props;
if (drawerItems.indexOf(route.key) > -1) {
return (
<TouchableOpacity style={styles.drawerItem} key={route.key} onPress={() => this.onItemPress(route)}>
<Text>{route.routeName}</Text>
</TouchableOpacity>
);
}
return null;
}
render() {
const { navigation, isFetching, drawerItemsTitle } = this.props;
return (
<View style={styles.container}>
{!isFetching && <View style={styles.drawerItemTitle}>
<Text style={styles.drawerItemTitleText}>{drawerItemsTitle}</Text>
</View>}
{!isFetching && <View>
{navigation.state.routes.map(route => this.renderDrawerItem(route))}
</View>}
{isFetching && <View style={styles.spinnerViewBg}>
<View style={styles.spinner}>
<ActivityIndicator
size="small"
animating
/>
</View>
</View>}
</View>
);
}
}
Here's a good example from Infinite Red. Tutorial link
As for the separators, it is basically a View with minimal height and certain width. I believe you can figure it out :) Good luck!

Related

react native : what is principle of stack navigation?

tired but i want to implement react native stack navigation.
import React, { useState } from "react";
const StackScreen = () => {
let InitialRoute;
let First;
let Second;
let Third;//assume they are screens of my app.
const [stack, setStack] = useState([InitialRoute]);
const replace = (screenName: any) => {
const tmp: Array<any> = stack.filter((el: any) => el = !screenName);
setStack([...stack, screenName]);
}
const navigate = (screenName: any) => {
stack.indexOf(screenName) == -1 ? setStack([...stack, screenName]) : replace(screenName);
}//navigate to another screen
const goBack = () => {
if (stack.length > 1) {
const tmp = [...stack];
tmp.pop();
setStack(tmp);
}
}//they are fuctions.
return stack[stack.length - 1];
}
const App = () => {
return (
<View>
<Appbar />
<StackScreen />
<BottomTab or anything i dont want to render while change screens./>
</View>
)
}
i make toy example even if it's not accurate with reality.
but i have question.
i enter the FirstScreen to SecondScreen. after a while, i pop the secondScreen.
in this case, my code will re-render the FirstScreen.
is the screen re-rendered in react - navigation?
if ans is no, how to i implement without rendering?
what is problem of my idea?
To Implement stack navigation in react native first add these packages https://reactnavigation.org/docs/getting-started according to your environment (EXPO CLI/React-native CLI) .
Stack Navigation Example
import * as React from 'react';
import { Button, View, Text } from 'react-native';
import { NavigationContainer } from '#react-navigation/native';
import { createNativeStackNavigator } from '#react-navigation/native-stack';
function HomeScreen({ navigation }) {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Home Screen</Text>
<Button
title="Go to Details"
onPress={() => navigation.navigate('Details')}
/>
</View>
);
}
function DetailsScreen() {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>press back button to go back</Text>
</View>
);
}
const Stack = createNativeStackNavigator();
function App() {
return (
<NavigationContainer>
<Stack.Navigator initialRouteName="Home">
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Details" component={DetailsScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}
export default App;
Stack navigation Basic concept
Stack Navigation Mean first Screen In last out. it mean, that that screen which display first and navigate to other and other screen, by pressing back button that first screen will display in last.
In react-navigation, your component will not automatically be re-rendered. See Refresh previous screen on goBack() for discussion on solutions to this issue in react-navigation.
This question is pretty general and I'm not sure how to answer. Can you give more detail on the feedback you're looking for?

Reloading an animation React-native-animatable library

guy's I'm using the react-native-animatable library. Basically, when I load my app the animation runs, however, when I go to another tab and return to the initial page the animation doesn't run anymore. I think it's because it' doesn't get reloaded anymore and I was wondering how to reload a component. As you can see the View has an animation prop which is the animation which has to be loaded.
import React, { Component } from 'react';
import { Text, Button, StyleSheet, Image, ImageBackground, TouchableOpacity } from 'react-native';
import Icon from 'react-native-vector-icons/MaterialCommunityIcons';
import LinearGradient from 'react-native-linear-gradient';
import {Fonts} from '../components/Fonts';
import { createAnimatableComponent, View, } from 'react-native-animatable';
class Home extends React.Component {
render() {
return (
<View style={styles.container}>
<View animation='bounceInLeft'
style={styles.container1}>
<View style={styles.card1}>
<ImageBackground
source={require('../images/pictures/runop.jpg')}
style={{width:'100%', height:200, }}>
<Text
style={{fontSize:30, alignSelf:'center', color:'white',
fontFamily:Fonts.Nunito}}
> Sport Schema's</Text>
</ImageBackground>
</View>
</View>
<View animation='bounceInRight' style={styles.container2}>
<View style={styles.card2}>
<Image
source={require('../images/pictures/foodop.jpg')}
style={{width:'100%', height:200}}/>
</View>
</View>
<View animation='bounceInLeft' style={styles.container3}>
<View style={styles.card3}>
<Image
source={require('../images/pictures/blogop.jpg')}
style={{width:'100%', height:200}}/>
</View>
</View>
</View>
);
}
}
Thanks for your help. I eventually got it to work with a different method.
I used the withNavigationFocus from react-navigation to get the isFocused props of the current screen. Then i just used an if statement if screen is focused then run animation else dont.
import {withNavigationFocus} from 'react-navigation';
class Profile extends React.Component {
constructor(props) {
super(props);
}
render()
// This checks if current screen is focused then run the animation otherwise dont.
{
if (this.props.isFocused && this.animation) {
this.animation.bounce(800)
}
return (
<View ref={(ref) => {this.animation = ref;}}
style={styles.container3}>
<View style={styles.card3}>
<Text> hii</Text>
</View>
</View>
);
}
}
});
export default withNavigationFocus(Profile); // <- dont forget this!!
If you are using react-navigation, below solution might work for you.
Create a function which would start the animation after some milliseconds and pass it to the next screen as params. Example,
SCREEN A
animateFunction() {
setTimeout(() => {
// start your animation
}, 100);
}
navigation.navigate(SCREEN_NAME, { startPrevScreenAnimation: animateFunction });
And in the next screen call that function when the component unmounts (componentWillUnmount()). Example,
SCREEN B
componentWillUnmount() {
this.props.navigation.state.params.startPrevScreenAnimation();
}
I said some milliseconds because you would want the animation to start once the screen transition is complete.
OR
Add a listener to your screen which fires an event when the screen is in focus.
if (this.props.navigation) {
this.willFocusSubscription = this.props.navigation.addListener(
'willFocus',
() => { // Run your animation },
);
}

React Native - Put child components into parent according to 'key' leads error

Here I am creating a react-native project which have installed react-navigation.
I have switch on the StackNavigator for pages across the application. Below is what I would like to archive.
I would like to use StackNavigator without using the provided header. Therefore I create a component <Header> like below:
export default class Header extends Component {
constructor(props) {
super(props);
}
detectKeyAndRender(item) {
if (item.key === "left") {
return <View style={leftContainerStyle}>{item}</View>;
} else if (item.key === "middle") {
return <View style={middleContainerStyle}>{item}</View>;
} else if (item.key === "right") {
return <View style={rightContainerStyle}>{item}</View>;
}
}
render() {
return (
<View key="header" style={headerContainerStyle}>
{ this.props.children.map((item) => ( this.detectKeyAndRender(item) )) }
</View>
);
}
}
Also for the common styles and container structure, I have created a component called <BaseContainer> like below:
import { getChildComponent } from 'helper.js';
export default class BaseContainer extends Component {
constructor(props) {
super(props);
}
render() {
return (
<SafeAreaView key="safe-area-view" style={safeAreaViewStyle}>
{ /* Please set the header from each page */ }
{ getChildComponent(this.props.children, "header") }
<View style={outerContainerStyle}>
<ScrollView contentContainerStyle={scrollViewStyle} alwaysBounceVertical={false}>
{ /* Page content goes in here */ }
{ getChildComponent(this.props.children, "content") }
</ScrollView>
</View>
</SafeAreaView>
);
}
}
And also the helper.js:
export function getChildComponent(children, key) {
return children.filter( (comp) => {
return comp.key === key;
});
}
Thus in other pages, I can call the header like below:
export default class FirstPage extends Component {
render() {
<BaseContainer>
<Header key="header">
<Text key="left">Left</Text>
<Text style={{color: '#fff', fontSize: 20}} key="middle">First Page</Text>
<Text key="right">Right</Text>
</Header>
<View key="content">
<Text>This is first page.</Text>
</View>
</BaseContainer>
}
}
export default class SecondPage extends Component {
render() {
<BaseContainer>
<Header key="header">
<Text key="left">Left</Text>
<Text style={{color: '#fff', fontSize: 20}} key="middle">Second Page</Text>
<Text key="right">Right</Text>
</Header>
<View key="content">
<Text>This is second page.</Text>
</View>
</BaseContainer>
}
}
This render method of <Header> will bring out an error but it's not fatal. The error is about the unique key:
Warning: Each child in an array or iterator should have a unique "key" props.
Check the render method of `Header`.
I know inside react native, every key should be unique (for performance issue).
Therefore I would like to ask is there any better method to put the specific child component into the header, or add the style according to a "key"? Appreciate for any help.
Please try to make the solution as simple as possible. Combine some common code together is what I would like to archive.
NOTE: I don't want to use the custom header inside react-navigation because the position it put is not what I want.
A simple way to get rid of the error would be to add a unique key to the View Components generated in detectKeyAndRender by adding a second parameter to it. So your function would be e.g. detectKeyAndRender(item, key).
Inside of the function simply add that key parameter to every returned View: <View key={key} ... />
Then when you actually map over the children, pass the index to the function:
{ this.props.children.map((item, i) => ( this.detectKeyAndRender(item, i) )) }
Unique keys should be given to each child element.
Header
export default class Header extends Component {
constructor(props) {
super(props);
}
detectKeyAndRender(item) {
/* you can use 'left,'middle' & 'right' as keys - they are different */
let { key } = item;
/* do validation on style hence you are not changing anything on view */
/* 'Conditional Operator' is used to simplify if...else...if...else */
let style =
key === "left"
? leftContainerStyle
: key === "middle"
? middleContainerStyle
: key === "right"
? rightContainerStyle
: middleContainerStyle /* default */;
return (
<View key={key} style={style}>
{item}
</View>
);
}
render() {
return (
<View key="header" style={headerContainerStyle}>
{this.props.children.map(item => this.detectKeyAndRender(item))}
</View>
);
}
}

Click Item in ListView React-Native not working

i am new in react-native and i want to press to to specific item in ListView, but when i click to item wich i want to select i didn't get console log message and i didn't get any errors so my code look like this
in renderRow my code look like this
renderRow(record) {
return (
<View style={styles.row}>
<TouchableHighlight onPress={() => this._pressRow()}>
<View style={styles.info}>
<Text style={styles.items}>{record.nom}</Text>
</View>
</TouchableHighlight>
</View>
);
}
and _pressRow function simple console log
_pressRow (rowID: number) {
console.log("clicked");
}
and render function
render() {
return (
<ScrollView scrollsToTop={false} style={styles.menu}>
<ListView
dataSource={this.state.dataSource}
renderRow={this.renderRow}
/>
</ScrollView>
);
}
how can i resolve this issue and thanks.
Are you using the autobind-decorator? Using your code as is the _pressRow method won't be triggered. When I add the autobind decorator or change _pressRow into a fat-arrow function the console.log does work for me:
import React, { Component } from 'react'
import { View, TouchableHighlight, Text, ScrollView, ListView } from 'react-native'
_pressRow = (rowID: number) => {
console.log("clicked")
}
class App extends Component {
constructor(props) {
super(props)
this.dataSource = new ListView.DataSource({
rowHasChanged: (r1, r2) => r1 !== r2,
})
this.state = {
dataSource: this.dataSource.cloneWithRows([
{ nom: 'a' },
{ nom: 'b' },
]),
}
}
renderRow(record) {
return (
<View>
<TouchableHighlight onPress={() => this._pressRow()}>
<View>
<Text>{record.nom}</Text>
</View>
</TouchableHighlight>
</View>
)
}
render() {
return (
<ScrollView scrollsToTop={false}>
<ListView
dataSource={this.state.dataSource}
renderRow={this.renderRow}
/>
</ScrollView>
)
}
}
export default App
change the this._pressRow() to this._pressRow.bind(this) if your function is in your Class
I used TouchableHighlight to wrap "un-pressable" component (accompany with changing this._pressRow to this._pressRow.bind(this)).
Moreover, some component (such as Text component) does not fill all the space of ListView row. So that onPress only works if you press right at text (it does not work if you press on the row location that does not have any text). So that it is useful to wrap with TouchableHighlight component.

How to pass the navigator object to the components used in the react natives root file?

Below is my app component:
/**
* Class app from where the app bootstraps
*/
export default class App extends Component {
constructor(props) {
super(props);
}
// This is where all your routes will be processed
renderScene(route, navigator) {
// Set a variable to get the route
let RouteComponent = route.component;
_navigator = navigator;
// With props return the components
return (
<RouteComponent navigator={navigator} {...route.passProps} />
);
}
static navigationBarRouteMapper = openControlPanel => ({
LeftButton: function(route, navigator, index, navState) {
return (
// Hamburger icon which on clicked opens the menu
<TouchableOpacity style={navBarStyle.left} onPress={() => openControlPanel()}>
<View>
{hamburgerIcon}
</View>
</TouchableOpacity>
);
},
RightButton: function(route, navigator, index, navState) {
return (
// cart menu
<TouchableWithoutFeedback onPress={() => dismissKeyboard()}>
<View style={navBarStyle.right}>
{cartIcon}
<View style={navBarStyle.counter}>
<Text style={navBarStyle.counterText}>20</Text>
</View>
</View>
</TouchableWithoutFeedback>
);
},
Title: function(route, navigator, index, navState) {
// Title of the route
return (
<TouchableWithoutFeedback onPress={() => dismissKeyboard()}>
<View style={navBarStyle.titleWrap}>
<Text style={navBarStyle.title}>{route.title.toUpperCase()}</Text>
</View>
</TouchableWithoutFeedback>
);
}
})
// Close the menu drawer
closeControlPanel() {
this._drawer.close();
}
// open the menu drawer
openControlPanel() {
this._drawer.open();
dismissKeyboard();
}
// On clicking the menu item, this function routes to that menu item page
getNavigator(route) {
this.refs.navigator.replace(route);
this.closeControlPanel();
}
render() {
return (
<Drawer
ref={ (ref) => { this._drawer = ref; } }
type="overlay"
content={<Menu navigator={this.getNavigator.bind(this)} menuItems={menu} closeControlPanel={this.closeControlPanel.bind(this)} />}
onOpenStart={() => dismissKeyboard()}
tapToClose={true}
openDrawerOffset={0.2}
panCloseMask={0.2}
panOpenMask={20}
acceptPan={true}
closedDrawerOffset={-3}
styles={drawerStyle}
tweenHandler={(ratio) => ({
// This code will maintain the opacity for main
// Whilst the opacity for the mainOverlay on the screen will be faded.
main: { opacity: 1 },
mainOverlay: {
opacity: ratio / 2,
backgroundColor: 'black',
}
})}>
<Navigator
initialRoute={homeScene}
renderScene={this.renderScene}
ref="navigator"
// Navbar of the app
navigationBar={
<Navigator.NavigationBar
routeMapper={App.navigationBarRouteMapper(this.openControlPanel.bind(this))}
style={navBarStyle.navBar}
/>
}
/>
<EditSearch />
</Drawer>
);
}
}
At the very bottom you'll see the <EditSearch> component which contains two text inputs. This component would be common to all the pages in the app except the home page.
Hence, I wanna know on which page or scene am I currently on so that I could check whether the scene/page is the home page or not. If it's the home page I would hide the component else I would show it for all other pages.
I tried passing the navigator via ref like so:
<EditSearch nav={this.refs.navigator} />
But, I get undefined on the <EditSearch> component and the view doesn't re-render when the page changes as it doesn't detect any state change.
I can do something like:
this.state = {
currentRoute: 'home'
}
And then change this state when route changes. But, I cannot change state within renderScene as setting state in renderScene will lead into infinite loop. If I could set this state with the title of the page when route changes, I could then send that state to <EditSearch> component.
I am pretty confused how can I pass current route information to this common component. Thanks in anticipation.
Okay, I found a solution but I am unsure if it's appropriate.
So below is my solution.
First I created the state and function to set that state:
constructor(props) {
super(props);
this.state = {
isHome: false
};
this.setIsHome = this.setIsHome.bind(this);
}
setIsHome(flag) {
// Set state to let component know whether the page is home or not
this.setState({
isHome: flag ? flag : false
});
}
Then passed the function to all the pages where those pages can either hide or show <EditSearch> component:
<RouteComponent navigator={navigator} {...route.passProps} setIsHome={this.setIsHome} />
Then I passed the isHome state to the <EditSearch> component:
<EditSearch isHome={ this.state.isHome } />
And this is how I call the function in the home component:
componentWillMount() {
this.props.setIsHome(true);
}
componentWillUnmount() {
this.props.setIsHome(false);
}
This is how I show/hide the component. I hope it's helpful. But, would be really nice to know how to pass navigator object to such components.
Here's a solution from Drawer Github issues.
Found a quick workaround for this but it is not ideal. If you wrap the
Navigator with the Drawer component, then inside of the
Navigator.NavigationBar you can set this.navigator = this.navigator ||
navigator and then you can pass the navigator to your content
component for the Drawer.
<Drawer
content={<Main navigator={this.navigator}/>}
type='overlay'>
<Navigator
renderScene={renderScene}
navigationBar={
<Navigator.NavigationBar
routeMapper={{
LeftButton: (route, navigator, index, navState) => {
this.navigator = this.navigator || navigator;
return (
<View>
<Text>Something</Text>
</View>
);
}
}}
/>
/>
</Drawer>
https://github.com/root-two/react-native-drawer/issues/187#issuecomment-231461585

Resources