Creation of a new component with navigation fields - react-native

I try to create a new react-native component that contains two button. I define two property next to going on the next page and prev to going in the precedent page but I have this error:
undefined is not an object (evaluating '_this.props.navigation.navigate')
I found so much question similar this but I don't found any valid solution.
This is the code:
import PropTypes from 'prop-types'
import React, { Component } from 'react';
import { View, StyleSheet, Button } from 'react-native';
export default class ButtonNavigation extends Component {
static propTypes = {
next: PropTypes.string,
prev: PropTypes.string,
}
render() {
const { next, prev } = this.props;
return (
<View style={styles.bottomContainer}>
<View style={styles.buttonContainer}>
<Button onPress={() => this.props.navigation.navigate(next)} title='Indietro' color='#00b0ff'/>
</View>
<View style={styles.buttonContainer}>
<Button onPress={() => this.props.navigation.navigate(prev)} title='Avanti' color='gold'/>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center'
},
bottomContainer: {
flex: 1,
flexDirection: 'row',
position: 'absolute',
bottom: 10,
padding: 20,
justifyContent: 'space-between'
},
text: {
fontSize: 30,
},
buttonContainer: {
flex: 1,
padding: 20,
justifyContent: 'center',
alignContent: 'center',
}
});
I include the object in the other page in this way:
<ButtonNavigation next={'NumberVehicle'} prev={'Home'}/>

In your <ButtonNavigation next={'NumberVehicle'} prev={'Home'}/> you need to add the navigation object as a prop. So something like:
<ButtonNavigation
next={'NumberVehicle'}
prev={'Home'}
navigation={this.props.navigation}
/>
(Make sure to replace this.props.navigation if it differs from the actual navigation object)
And after you can do:
const { next, prev, navigation } = this.props;
and
navigation.navigate(..)

You can change this
<ButtonNavigation next={this.props.navigation.navigate('NumberVehicle')} prev={this.props.navigation.navigate('Home')}/>
<View style={styles.bottomContainer}>
<View style={styles.buttonContainer}>
<Button onPress={() => this.props.next} title='Indietro' color='#00b0ff'/>
</View>
<View style={styles.buttonContainer}>
<Button onPress={() => this.props.prev} title='Avanti' color='gold'/>
</View>
</View>

Related

How to pass radio button values from a modal component to a parent component: React Native

I have a modal component (Sortby.js) with a FlatList, and each list item has two radio buttons with 'asc' and 'desc' values. When a user selects a radio button, the state in the modal component is updated with the index of the list and the value of the button pressed. A user can only select one button.
When the user presses "OK" on the modal, I need to somehow get the index of the list and value of the radio button ('asc' or 'desc') sent to the parent component. How can I do this? Been stuck on this for ages now. Code is below, link to the Snack is here: https://snack.expo.dev/#steph510/send-radio-button-index-and-values-to-parent-on-modal-close
Sortby.js
import {View, Text, Modal, StyleSheet, Button, FlatList,} from "react-native";
import { RadioButton } from "react-native-paper";
export default class Sortby extends React.Component {
constructor(props) {
super(props);
}
state = {
selectedIndex: 0,
radioButtonValue: 'asc',
};
onRadiochange = (index, value) => {
this.setState({
radioButtonValue: value,
selectedIndex: index
});
};
render() {
return (
<Modal visible={this.props.visible} transparent={true}>
<View style={styles.modalStyles}>
<View style={styles.fieldsContainer}>
<FlatList
data={this.props.showInGridViewList()}
extraData={this.state}
renderItem={(itemData) => {
const index = itemData.index;
return (
<View style={styles.fieldItem}>
<Text style={styles.fieldText}>{itemData.item.text}</Text>
<View style={styles.radioButtonContainer}>
<RadioButton.Group onValueChange={value => this.onRadiochange(index, value)}>
<View style={styles.singleRadioButtonContainer}>
<Text>Asc</Text>
<RadioButton
color='#5d86d7'
value="asc"
status={ this.state.selectedIndex === index && this.state.radioButtonValue === 'asc' ? 'checked' : 'unchecked'}
/>
</View>
<View style={styles.singleRadioButtonContainer}>
<Text>Desc</Text>
<RadioButton
color='#5d86d7'
value="desc"
status={ this.state.selectedIndex === index && this.state.radioButtonValue === 'desc' ? 'checked' : 'unchecked'}
/>
</View>
</RadioButton.Group>
</View>
</View>
);
}}
alwaysBounceVertical={false}
/>
</View>
<View style={styles.buttonContainer}>
<View style={styles.button}>
<Button title="OK" color={"#5d86d7"} onPress={this.props.onOK}></Button>
</View>
<View style={styles.button}>
<Button
title="Cancel"
color={"#5d86d7"}
onPress={this.props.onCancel}
></Button>
</View>
</View>
</View>
</Modal>
);
}
}
const styles = StyleSheet.create({
modalStyles: {
height: "auto",
width: "90%",
flexDirection: "column",
justifyContent: "flex-start",
alignItems: "center",
backgroundColor: "#fff",
borderColor: "#777",
borderWidth: 1,
marginLeft: 20,
},
fieldsContainer: {
width: "100%",
},
fieldItem: {
flexDirection: "row",
alignItems: "center",
justifyContent: "space-between",
paddingLeft: 12,
borderBottomWidth: 1,
borderBottomColor: "#ebebeb",
},
fieldText: {
color: "#444",
},
radioButtonContainer: {
flexDirection: "row",
justifyContent: "flex-start",
alignItems: "center",
},
singleRadioButtonContainer: {
flexDirection: "row",
justifyContent: "center",
alignItems: "center",
marginRight: 10,
},
buttonContainer: {
flexDirection: "row",
justifyContent: "center",
alignItems: "center",
},
button: {
width: "100%",
marginHorizontal: 8,
},
});
App.js
import * as React from 'react';
import { Text, View, StyleSheet, Button } from 'react-native';
import Constants from 'expo-constants';
// You can import from local files
import Sortby from './components/Sortby';
// or any pure javascript modules available in npm
import { Card } from 'react-native-paper';
export default class App extends React.Component {
state = {
showSortby: false,
};
startSortByHandler = () => {
this.setState({
showSortby: true,
});
};
endSortByHandler = () => {
this.setState({
showSortby: false,
});
};
getSortValues = () => {
this.endSortByHandler()
}
getShowInGridViewList = () => {
const gridFieldArray = [
{"text":"Organization","key":"0.7204364607892255"},
{"text":"Document No","key":"0.11948720939854396"},
{"text":"Warehouse","key":"0.5981352662697218"},
{"text":"Business Partner","key":"0.3617080891091381"},
{"text":"Partner Address","key":"0.9242697027340077"},
{"text":"Movement Date","key":"0.19100558269330914"}]
return gridFieldArray;
};
render() {
return (
<View style={styles.container}>
<Card>
<Sortby
visible={this.state.showSortby}
onCancel={this.endSortByHandler}
showInGridViewList={this.getShowInGridViewList}
onOK={this.getSortValues}
></Sortby>
<Button title={'Open Modal'} onPress={this.startSortByHandler}></Button>
</Card>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
paddingTop: Constants.statusBarHeight,
backgroundColor: '#ecf0f1',
padding: 8,
}
});
There is a pattern called Lifting Up State where sharing the state is accomplished by moving it up to the closest common ancestor of the components that need it.
A state is created in the parent component and the setter for that state is passed to the child component.
That way, whenever the state is setted in the child component, you can manage it in the parent component too.
Create state in parent component and pass it to your child component

How to get a param of an id using react-navigation v5?

I'm trying to update an app I've developed using react-navigation v4.
Using react-navigation v4 I could get the id using something like console.log(navigation.getParam('id'));
But when I tried the same after updating to v5 of react-navigation it shows me an error and I can't find the right way to get the param id
import React from 'react';
import { View, Text, StyleSheet } from 'react-native';
export default function ShowScreen({ navigation }) {
console.log(navigation.getParam('id'));
return (
<View style={styles.container}>
<Text>Show Screen</Text>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center'
}
});
The other screen where the id is located is it:
import React, { useContext } from 'react';
import { View, Text, StyleSheet, Button } from 'react-native';
import { FlatList, TouchableOpacity } from 'react-native-gesture-handler';
import { Context } from '../../context/BlogContext';
import Icon from 'react-native-vector-icons/Feather'
export default function IndexScreen({ navigation }) {
const { state, addBlogPost, deleteBlogPost } = useContext(Context);
return (
<View style={styles.container}>
<Button
title="Add Post"
onPress={addBlogPost}
/>
<FlatList
data={state}
keyExtractor={(blogPost) => blogPost.title}
renderItem={({ item }) => {
return (
<TouchableOpacity onPress={
() => navigation.navigate('ShowScreen',
{ id: item.id })}>
<View style={styles.row}>
<Text style={styles.title}>
{item.title} -
{item.id}
</Text>
<TouchableOpacity onPress={() => deleteBlogPost(item.id)}>
<Icon style={styles.icon}
name="trash"
/>
</TouchableOpacity>
</View>
</TouchableOpacity>
);
}}
/>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
marginTop: 30,
justifyContent: 'center',
alignItems: 'center'
},
row: {
flexDirection: 'row',
justifyContent: 'space-between',
paddingHorizontal: 10,
paddingVertical: 20,
borderTopWidth: 1,
borderColor: '#333'
},
title: {
fontSize: 18
},
icon: {
fontSize: 24
}
});
You can get params using route.params in react navigation v5 more on that read here
just update your code to this
import React from 'react';
import { View, Text, StyleSheet } from 'react-native';
export default function ShowScreen({ navigation,route }) {
const {id} =route.params; //you will get id here via route
return (
<View style={styles.container}>
<Text>Show Screen</Text>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center'
}
});
you can simply access your parameters like this
const id = this.props.route.params.id
inside this.props.route.params you will get all paramaters :)

React native align icon and text

for the love of God, every styling i put on the stylesheet doesn't change anything.
i tried style the view, sectionheader & sectionbox but no luck
i want to align 4 icons in the boxsection and text below then as such, please any help would be appreciated.
export default class HomePage extends Component {
render() {
return (
<View>
<SectionHeader title={'Food'} />
<View >
<SectionBox >
<Icon style={styles.icons} name="icon name" size={50} />
<Icon style={styles.icons} name="icon name" size={50} />
<Icon style={styles.icons} name="icon name" size={50} />
<Icon style={styles.icons} name="icon name" size={50} />
<Text style={styles.sectiontext}>burgers</Text>
</SectionBox>
</View>
const styles = StyleSheet.create({
icons: {
flexDirection: 'row',
paddingTop: 7,
paddingLeft: 5,
},
sectiontext: {
fontSize: 15,
fontWeight: 'bold',
paddingLeft: 5,
alignItems: 'center',
}
});
For the icons-containing-box you would need to indicate flexDirection and flexWrap, not directly on the icon's style. Then to get the text below each icon you need to wrap icon and text in its own view and give that 'column' direction.
import * as React from 'react';
import { Text, View, StyleSheet } from 'react-native';
import { Ionicons } from '#expo/vector-icons';
import { Constants } from 'expo';
const ICON_SIZE = 70;
const FONT_SIZE = 18;
const getItem = () => (
<View style={styles.iconStyle}>
<Ionicons name="md-checkmark-circle" size={ICON_SIZE} color="green" />
<Text style={styles.textStyle}>name</Text>
</View>
);
export default class App extends React.Component {
render() {
return (
<View style={styles.container}>
<View style={styles.iconContainer}>
{getItem()}
{getItem()}
{getItem()}
{getItem()}
</View>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
iconContainer: {
width: ICON_SIZE * 2,
flexDirection: 'row',
flexWrap: 'wrap',
},
iconStyle: {
flexDirection: 'column',
alignItems: 'center',
padding: 5,
},
textStyle: {
fontSize: FONT_SIZE,
},
});
You need a view for each group of icon and text with a flexDirection: 'column' and one another view for each row (or column), like the example below:
https://snack.expo.io/HJY7IWsFm
Other option is to use a GridView lib:
https://github.com/saleel/react-native-super-grid

Progress Dialog in ReactNative

I'm pretty new to ReactNative world. Im struggling to find an api or a library that shows the Progress Dialog as below in React Native. I believe ActivityIndicator can be used, but it does not show as overlay. Can anyone help me how can I show as an overlay using styles or if there is good library to make this.
Thanks
Here is the code to Open Prgressbar:
import React from 'react';
import { Modal, View, Text, ActivityIndicator, Button } from 'react-native';
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = { isProgress: false }
}
openProgressbar = () => {
this.setState({ isProgress: true })
}
render() {
return (
this.state.isProgress ?
<CustomProgressBar />
:
<View style={{ flex: 1, backgroundColor: '#fff', alignItems: 'center', justifyContent: 'center' }}>
<Button title="Please click here to Open ProgressBar" onPress={this.openProgressbar} />
</View>
);
}
}
const CustomProgressBar = ({ visible }) => (
<Modal onRequestClose={() => null} visible={visible}>
<View style={{ flex: 1, backgroundColor: '#dcdcdc', alignItems: 'center', justifyContent: 'center' }}>
<View style={{ borderRadius: 10, backgroundColor: 'white', padding: 25 }}>
<Text style={{ fontSize: 20, fontWeight: '200' }}>Loading</Text>
<ActivityIndicator size="large" />
</View>
</View>
</Modal>
);
Expo url for live demo
https://snack.expo.io/#jitendra.mca13/progress-bar-demo
I would approach this by using the React Native Modal component with two Views.
This solution is a React solution and not native, so you will need to style your Modal accordingly for each platform.
import React from 'react';
import {
Modal,
View,
StyleSheet,
Text,
ActivityIndicator
} from 'react-native';
const ProgressDialog = ({ visible }) => (
<Modal
visible={visible}
>
<View style={styles.container}>
<View style={styles.content}>
<Text style={styles.title}>Please Wait</Text>
<View style={styles.loading}>
<View style={styles.loader}>
<ActivityIndicator size="large" />
</View>
<View style={styles.loadingContent}>
<Text>Loading</Text>
</View>
</View>
</View>
</View>
</Modal>
);
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'rgba(0, 0, 0, .5)',
alignItems: 'center',
justifyContent: 'center',
},
content: {
padding: 35,
backgroundColor: 'white'
},
title: {
fontSize: 18,
fontWeight: 'bold',
},
loading: {
flexDirection: 'row',
alignItems: 'center',
},
loader: {
flex: 1,
},
loadingContent: {
flex: 3,
fontSize: 16,
paddingHorizontal: 10,
}
})
export default ProgressDialog;
Here's a demo on Expo, of course you will probably want to tweak the CSS.
Android & iOS solution
Create a directory called ProgressDialog
Create index.ios.js and index.android.js
Paste the above code into both index.ios.js and index.android.js
Make CSS changes for iOS
You can use the below npm package which is a very easy and attractive loader with many options.
https://github.com/maxs15/react-native-spinkit
import React from 'react';
import { StyleSheet, View } from "react-native";
import Spinner from "react-native-spinkit";
export default LoadingCmpBig = props => {
return (
<View style={styles.container}>
<StatusBar barStyle="default" />
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
//backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
}
});
You can use this component and use it where you want to show the Progress. You just have to maintain the state for visible the loading or not in your render function of the component.

React-native elements search bar ref to use focus() method for instance is undefined

I followed the doc of the react-native-elements library but I get this.search as an undefined object...
searchbar react-native-elements
class SearchBarTest extends Component {
componentDidMount()
{
this.search.focus()//search is undefined
}
render(){
<SearchBar
ref={search => this.search = search}
...
/>
}
}
Any idea how to make this working?
EDIT:
Adding full context of the code.
The idea is having a component with a header and body. The header has an optional search bar or a title and two buttons depending on the props search.
If search props is true, then I display the searchBar.
PARENT COMPONENT:
import React, {Component} from 'react';
import {StyleSheet, View, Text, TouchableWithoutFeedback, Keyboard} from 'react-native';
import Icon from 'react-native-vector-icons/Ionicons';
import DismissableKeyboardContainer from '../../components/DismissableKeyboardContainer';
export default class Search extends Component {
static navigationOptions = () => {
return {
tabBarIcon: ({tintColor}) => (
<Icon name="ios-search-outline" size={25} color={tintColor}/>
)
}
};
render() {
someMethod = () => {
console.log('hello');
}
return (
<DismissableKeyboardContainer search={true}>
<View style={{flex: 1, alignItems: 'center'}}>
<Text>Hello world</Text>
</View>
</DismissableKeyboardContainer>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
}
});
COMPONENT where the actual searchBar component is used:
import React, { Component} from 'react';
import { Keyboard, TouchableWithoutFeedback, TouchableOpacity, View, Text, StyleSheet} from 'react-native';
import { SearchBar } from 'react-native-elements'
import Icon from 'react-native-vector-icons/Ionicons';
import PropTypes from 'prop-types';
class DismissableKeyboardContainer extends Component {
static defaultProps = {
title: '',
search: false,
buttonLeft: '',
buttonRight: '',
borderShadow: true,
headerStyle:{}
};
componentDidMount()
{
}
render() {
let { title, search, buttonLeft, buttonRight, headerStyle, borderShadow } = this.props;
return (
<TouchableWithoutFeedback onPress={() => Keyboard.dismiss()}>
<View style={{ flex: 1 }}>
<View style={[styles.headerContainer, borderShadow ? styles.borderShadow : '', headerStyle]}>
{
!search && <View style={{position: 'absolute', padding: 10, bottom: 0, flex: 1, flexDirection: 'row', alignItems: 'center', justifyContent: 'center'}}>
<TouchableOpacity style={{ flex: 0.2, alignItems: 'center'}}>
{
buttonLeft != "" && <Icon name={buttonLeft} size={25} color="grey" />
}
</TouchableOpacity>
<View style={{ flex: 1, alignItems: 'center' }}>
<Text style={styles.title}>{title}</Text>
</View>
<TouchableOpacity style={{ flex: 0.2, alignItems: 'center' }}>
{
buttonRight != "" && <Icon name={buttonRight} size={25} color="grey" />
}
</TouchableOpacity>
</View>
}
{
search && <SearchBar
ref={search => this.search = search}
containerStyle={{ backgroundColor: 'transparent', borderTopWidth: 0, borderBottomWidth: 0 }}
inputStyle={{ backgroundColor: 'white' }}
lightTheme
onChangeText={someMethod}
placeholder='Type Here...'
/>
}
</View>
<View style={{ flex: 1, backgroundColor: 'transparent'}}>
{this.props.children}
</View>
</View>
</TouchableWithoutFeedback>
);
}
}
const styles = StyleSheet.create({
headerContainer: {
flex: 0.12,
justifyContent: 'flex-end',
backgroundColor: 'white' ,
},
borderShadow: {
borderColor: '#ddd',
borderBottomWidth: 0,
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.2,
shadowRadius: 1,
elevation: 1,
},
title: {
fontSize: 18,
fontWeight: 'bold'
}
});
DismissableKeyboardContainer.propTypes = {
title: PropTypes.string.isRequired,
search: PropTypes.bool.isRequired,
buttonLeft: PropTypes.string.isRequired,
buttonRight: PropTypes.string.isRequired,
headerStyle: PropTypes.any,
};
export default DismissableKeyboardContainer;
I think I found the issue. As my SearchScreen is part of a TabBar from react-navigation, this.search.focus() error was appearing at the initial loading of the app. The Search screen being the second tab of the tabBar, I guess this is the reason why search couldn't be found as the screen was not active.
Found this answer: Trigger event on tabBar screen display
Which helped me to launch the this.search.focus() only when the screen Search is called under the componentDidUpdate() method
Please see code below:
1- First on my root navigator, I track react-navigation screen changes with onNavigationStateChange and screenProps
<Root
onNavigationStateChange={(prevState, currentState, action) => {
let currentScreen = action.routeName;
this.setState({ currentScreen })
}}
screenProps={{ currentScreen: this.state.currentScreen }}
/>
2- Then on my Parent component, I pass the currentScreen prop from I setup above in react-navigation
<DismissableKeyboardContainer search={true} currentScreen={this.props.screenProps.currentScreen}>
<View style={{flex: 1, alignItems: 'center'}}>
<Text>Hello world</Text>
</View>
</DismissableKeyboardContainer>
3- Now, I'm checking the screen being displayed and launch this.search.focus() only if the screen Search is displayed inside my DismissableKeyboardContainer child component.
componentDidUpdate() {
this.props.currentScreen === "Search" ? this.search.focus() : ''
}