React component not re-rendering on state change using setState - react-native

I have a HomeScreen component which has button. I am trying to popup a modelview(Seperate component ie PopUpView) on the button click. In PopUpView i am sending its visibility as prop (isVisible).On the click of the button i am trying to change the state value popUpIsVisible from false to true. Hoping that this would re-render and popup my model view(Note this is working fine if i explicitly pass true). However with the state change it look like the render function is not being called and the popUp is not being displayed. Thanks for your help in advance
import React from 'react';
import { View, StyleSheet, Text, Button, TouchableHighlight, Alert, Dimensions} from 'react-native';
import { createStackNavigator, createAppContainer } from 'react-navigation';
import PopUpView from './src/PopUpView';
class HomeScreen extends React.Component {
constructor(props) {
super(props);
this.state = {
popUpIsVisible: false,
};
}
setPopUpIsVisible(isVisible){
this.setState({popUpIsVisible: isVisible });
}
render() {
this.setPopUpIsVisible = this.setPopUpIsVisible.bind(this);
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center', backgroundColor:"blue" }}>
<Text>Home Screen</Text>
<PopUpView isVisible={this.state.popUpIsVisible}/>
<Button onPress={() => {this.setPopUpIsVisible(true)}} title="Open PopUp Screen"/>
</View>
);
}
}
const RootStack = createStackNavigator(
{
Home: HomeScreen
},
{
initialRouteName: 'Home',
}
);
const AppContainer = createAppContainer(RootStack);
export default class App extends React.Component {
render() {
return <AppContainer />;
}
}
PopUpView.js
import React from 'react';
import { View, Modal,StyleSheet, Text, TouchableOpacity, Dimensions} from 'react-native';
import { TabView, TabBar,SceneMap } from 'react-native-tab-view';
import Icon from 'react-native-vector-icons/SimpleLineIcons';
const FirstRoute = () => (
<View style={{ flex: 1, backgroundColor: '#ff4081' }} />
);
const SecondRoute = () => (
<View style={{ flex: 1, backgroundColor: '#673ab7' }} />
);
const ThirdRoute = () => (
<View style={{ flex: 1, backgroundColor: '#673ab7' }} />
);
export default class PopUpView extends React.Component {
constructor(props) {
super(props);
this.state = {
modalVisible:this.props.isVisible,
index: 0,
routes: [
{ key: 'first', title: 'HIGHLIGHTS' },
{ key: 'second', title: 'AMENITIES' },
{ key: 'third', title: 'FACILITIES' },
],
};
}
setModalVisible(visible) {
this.setState({modalVisible: visible});
}
renderHeader = props => <TabBar
{...props}
indicatorStyle={{backgroundColor: 'red'}}
tabStyle={styles.bubble}
labelStyle={styles.noLabel}
/>;
render() {
return (
<Modal
animationType="slide"
transparent={true}
visible={this.state.modalVisible}
onRequestClose={() => {
Alert.alert('Modal has been closed.');
}}>
<View style={styles.container}>
<View style={styles.navBar}>
<Text style={styles.navBarTitle}>Test</Text>
<TouchableOpacity
onPress={() => {
this.setModalVisible(!this.state.modalVisible);
}}>
<Icon style={styles.closeButton} name="close" size={35} color="grey" />
</TouchableOpacity>
</View>
<TabView
navigationState={this.state}
renderScene={SceneMap({
first: FirstRoute,
second: SecondRoute,
third: ThirdRoute,
})}
onIndexChange={index => this.setState({ index })}
initialLayout={{ width: Dimensions.get('window').width }}
renderTabBar={props =>
<TabBar
{...props}
style={{ backgroundColor: 'white' }}
indicatorStyle={{backgroundColor: 'black'}}
tabStyle={styles.bubble}
labelStyle={styles.label}
/>
}
/>
</View>
</Modal>
);
}
}
const styles = StyleSheet.create({
container: {
flex:1,
margin: 50,
marginLeft: 20,
marginRight: 20,
marginBottom: 20,
backgroundColor: "white",
borderWidth: 1,
borderColor: "grey",
flexDirection: 'column'
},
navBar:{
height:70,
justifyContent: 'space-between',
alignItems: 'center',
flexDirection: 'row',
borderBottomColor: 'lightgrey',
borderBottomWidth: 1,
},
navBarTitle:{
fontSize: 25,
fontFamily: 'Optima',
paddingLeft:15,
},
closeButton:{
paddingRight:12,
},
label: {
color: 'black'
}
})

The problem with your code is that you are using the state inside the PopUpView which does not change when you change the external prop. to fix this you should use the componentwillreceiveprops and update your state accordingly.
componentWillReceiveProps(nextProps){
if(this.props.isVisible!=nextProps.isVisible){
this.setState({modalVisible:nextProps.isVisible})
}
}
The better approach will be using the this.props.isVisible as the visible prop for the Model.
In this scenario you will have to pass a function as a prop to popupview which will set the popUpIsVisible to false.
Something like below
<PopUpView isVisible={this.state.popUpIsVisible}
onDismiss={()=>{this.setState({popUpIsVisible:false})}}/>
You can call the onDismiss inside the child as
<Modal visible={this.props.isVisible}>
<TouchableHighlight
onPress={() => {
this.props.onDismiss();
}}>
<Text>Hide Modal</Text>
</TouchableHighlight>
</Modal>
The second approach is better as the visibility of the child is controlled by the parent.

Add below line into constructor after state defining
this.setPopUpIsVisible = this.setPopUpIsVisible.bind(this);

Related

undefined is not an object (evaluating '_this.props.navigation') Expo React Native

I am getting undefined is not an object evaluating _this.props.navigation. Here is my code. I want to use Flatlist to call the screen 'PlaceorderScreen.js' number of times from the component 'NearbyLSP.js' present in the 'HomeScreen.js'. Also I had referred many solutions for the above problem but yet I didn't get how to get through it. I am a beginner in react-native, So I had used expo for app development.
HomeScreen.js
import React from "react";
import {
View,
Text,
StyleSheet,
SafeAreaView,
TextInput,
Platform,
StatusBar,
ScrollView,
TouchableOpacity,
Dimensions,
Animated,
} from "react-native";
import Icon from 'react-native-vector-icons/Ionicons'
import Category from './Category'
import Expressmode from './Expressmode'
import { FlatList } from "react-native-gesture-handler";
import NearbyLSP from "./NearbyLSP";
const { width } = Dimensions.get('window')
const data = [
{id:'1',name:'LSP1',phone:'1234',email:'abc#gmail.com'},
{id:'2',name:'LSP2',phone:'0234',email:'abc2#gmail.com'},
{id:'3',name:'LSP3',phone:'2234',email:'abc3#gmail.com'}
]
const renderList = ((item) =>{
return <TouchableOpacity
onPress={()=> this.props.navigation.navigate('PlaceOrder')}
activeOpacity={0.7}>
<NearbyLSP name={item.name}/>
</TouchableOpacity>
})
export default class HomeScreen extends React.Component {
componentDidMount() {
this.scrollY = new Animated.Value(0)
this.startHeaderHeight = 80
this.endHeaderHeight = 50
if (Platform.OS == 'android') {
this.startHeaderHeight = 100 + StatusBar.currentHeight
this.endHeaderHeight = 70 + StatusBar.currentHeight
}
this.animatedHeaderHeight = this.scrollY.interpolate({
inputRange: [0, 50],
outputRange: [this.startHeaderHeight, this.endHeaderHeight],
extrapolate: 'clamp'
})
this.animatedOpacity = this.animatedHeaderHeight.interpolate({
inputRange: [this.endHeaderHeight, this.startHeaderHeight],
outputRange: [0, 1],
extrapolate: 'clamp'
})
this.animatedTagTop = this.animatedHeaderHeight.interpolate({
inputRange: [this.endHeaderHeight, this.startHeaderHeight],
outputRange: [-30, 10],
extrapolate: 'clamp'
})
this.animatedMarginTop = this.animatedHeaderHeight.interpolate({
inputRange: [this.endHeaderHeight, this.startHeaderHeight],
outputRange: [50, 30],
extrapolate: 'clamp'
})
}
render() {
return (
<SafeAreaView style={{ flex: 1 }}>
<View style={{ flex: 1 }}>
<Animated.View style={{ height: this.animatedHeaderHeight, backgroundColor: 'white', borderBottomWidth: 1, borderBottomColor: '#dddddd' }}>
<View style={{
flexDirection: 'row', padding: 10,
backgroundColor: 'white', marginHorizontal: 10,
shadowOffset: { width: 0, height: 0 },
shadowColor: 'black',
shadowOpacity: 0.2,
elevation: 1,
marginTop: null
}}>
<Icon name="ios-search" size={25} style={{ marginRight: 10 ,marginTop:5 , opacity:0.6}} />
<TextInput
underlineColorAndroid="transparent"
placeholder="Find LSP"
placeholderTextColor="grey"
style={{ flex: 1, fontWeight: '700', backgroundColor: 'white' }}
/>
</View>
</Animated.View>
<ScrollView
scrollEventThrottle={16}
onScroll={Animated.event(
[
{ nativeEvent: { contentOffset: { y: this.scrollY } } }
]
)}
>
<View style={{ flex: 1, backgroundColor: 'white', paddingTop: 20 }}>
<Text style={styles.text}>
Laundry Service Provider Type
</Text>
<View style={{ height: 130, marginTop: 20 }}>
<ScrollView
horizontal={true}
showsHorizontalScrollIndicator={false}
>
<TouchableOpacity onPress={()=> this.props.navigation.navigate('PlaceOrder')} activeOpacity={0.7}><Category name="Dry Clean" /></TouchableOpacity>
<TouchableOpacity onPress={()=> this.props.navigation.navigate('PlaceOrder')} activeOpacity={0.7}><Category name="Iron"/></TouchableOpacity>
<TouchableOpacity onPress={()=> this.props.navigation.navigate('PlaceOrder')} activeOpacity={0.7}><Category name="Dry Clean + Iron" /></TouchableOpacity>
</ScrollView>
</View>
<View style={{ marginTop: 40}}>
<Text style={styles.text}>
Find Nearby LSP
</Text>
<Text style={{ fontWeight: '100', marginTop: 10,paddingHorizontal: 20 }}>
Verified LSP Providers
</Text>
<View style={{ marginTop: 20, paddingHorizontal: 15
}}>
<FlatList
data={data}
renderItem={({item})=>{
return renderList(item)
}}
keyExtractor={item=>item.id}
navigation={this.props.navigation}
/>
</View>
</View>
</View>
<View style={{ marginTop: 40 }}>
<Text style={styles.text}>
Express Mode
</Text>
<View style={{ paddingHorizontal: 20, marginTop: 20, flexDirection: 'row', flexWrap: 'wrap', justifyContent: 'space-between' }}>
<Expressmode width={width}
name="LSP1"
type="Dry Clean"
price={82}
rating={4}
/>
<Expressmode width={width}
name="LSP2"
type="Iron + Dry Clean"
price={82}
rating={4}
/>
<Expressmode width={width}
name="LSP3"
type="Iron"
price={82}
rating={4}
/>
</View>
</View>
</ScrollView>
</View>
</SafeAreaView>
);
;
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: "center",
alignItems: "center"
},
text: {
fontSize: 24,
fontWeight:'700',
paddingHorizontal: 20
}
});
'HomeScreen.js' contains another component where I had successfully used the navigation to navigate to 'PlaceorderScreen.js'. But when I call this navigation inside Flatlist it gives the error.
NearbyLSP.js
import React from "react";
import { View, Text, StyleSheet, TouchableOpacity, Dimensions, Image } from "react-native";
import { Card } from "react-native-paper" ;
const { width } = Dimensions.get('window')
export default class NearbyLSP extends React.Component {
render() {
return (
<View>
<Card style={styles.mycard}>
<View style={{ width: width - 40, height: 200}} >
<Image
style={{ flex: 1, height: null, width: null, resizeMode: 'cover', borderRadius: 5, borderWidth: 1, borderColor: '#dddddd' }}
source={require('../assets/a.jpeg')}
/>
<Text>{this.props.name}</Text>
</View>
</Card>
</View>
);
}
}
const styles = StyleSheet.create({
mycard: {
margin: 5
}
});
App.js
import React from "react";
import { createAppContainer, createSwitchNavigator , createBottomTabNavigator } from "react-navigation";
import { createStackNavigator } from "react-navigation-stack";
import LoadingScreen from "./screens/LoadingScreen";
import LoginScreen from "./screens/LoginScreen";
import SignupScreen from "./screens/SignupScreen";
import HomeScreen from "./screens/HomeScreen";
import AboutScreen from "./screens/AboutScreen";
import PastorderScreen from "./screens/PastorderScreen";
import TrackScreen from "./screens/TrackScreen";
import ProfileScreen from "./screens/ProfileScreen";
import PlaceorderScreen from "./screens/PlaceorderScreen";
import MapsScreen from "./screens/MapsScreen";
import NearbyLSP from "./screens/NearbyLSP";
import { Ionicons , MaterialIcons } from '#expo/vector-icons';
import * as firebase from "firebase";
const firebaseConfig = {
apiKey: "AIzaSyAhwjD3jodBg6JGJ7Oo72r7-m2T5wwPaUg",
authDomain: "a4project-2d896.firebaseapp.com",
databaseURL: "https://a4project-2d896.firebaseio.com",
projectId: "a4project-2d896",
storageBucket: "a4project-2d896.appspot.com",
messagingSenderId: "181932238433",
appId: "1:181932238433:web:aa3f6fa3947976a8352c20",
measurementId: "G-SW6N6N8JYK"
};
firebase.initializeApp(firebaseConfig);
const HomeStack = createStackNavigator({
Home: HomeScreen,
NearbyLSP:NearbyLSP,
PlaceOrder:PlaceorderScreen
},
{
defaultNavigationOptions: {
title:'Home',
headerStyle: {
backgroundColor:'#12B2C2',
},
headerTintColor:'#fff',
headerTitleStyle:{
fontWeight:'bold'
}
}
})
const OrderStack = createStackNavigator({
Order: TrackScreen,
Maps: MapsScreen,
},
{
defaultNavigationOptions: {
title:'Orders',
headerStyle: {
backgroundColor:'#12B2C2',
},
headerTintColor:'#fff',
headerTitleStyle:{
fontWeight:'bold'
}
}
})
const PastorderStack = createStackNavigator({
Pastorder: PastorderScreen,
},
{
defaultNavigationOptions: {
title:'Pastorder',
headerStyle: {
backgroundColor:'#12B2C2',
},
headerTintColor:'#fff',
headerTitleStyle:{
fontWeight:'bold'
}
}
})
const ProfileStack = createStackNavigator({
Profile: ProfileScreen,
About: AboutScreen
},
{
defaultNavigationOptions: {
title:'Profile',
headerStyle: {
backgroundColor:'#12B2C2',
},
headerTintColor:'#fff',
headerTitleStyle:{
fontWeight:'bold'
}
}
})
const AuthStack = createStackNavigator({
Login: LoginScreen,
Signup: SignupScreen
},
{
defaultNavigationOptions: {
headerStyle: {
backgroundColor:'#12B2C2',
},
headerTintColor:'#fff',
headerTitleStyle:{
fontWeight:'bold'
}
}
})
const myTabs= createBottomTabNavigator({
Home:HomeStack,
Orders:OrderStack,
Profile:ProfileStack,
Pastorder:PastorderStack
},
{
defaultNavigationOptions: ({ navigation })=> {
return {
tabBarIcon:({tintColor})=>{
const { routeName } = navigation.state;
let myicon
if(routeName=="Home"){
myicon='md-home'
return <Ionicons name={myicon} size={30} color={tintColor}/>
}else if(routeName=="Profile"){
myicon='md-person'
return <Ionicons name={myicon} size={30} color={tintColor}/>
}else if(routeName=="Orders"){
myicon='md-book'
return <Ionicons name={myicon} size={30} color={tintColor}/>
}else if(routeName=="Pastorder"){
myicon='library-books'
return <MaterialIcons name={myicon} size={30} color={tintColor}/>
}
}
};
}
});
export default createAppContainer(
createSwitchNavigator(
{
Loading: LoadingScreen,
Auth: AuthStack,
App: myTabs
},
{
initialRouteName: "Loading"
},
)
);
Just writing navigation={this.props.navigation} in Flatlist won't let you navigate to the desired screen. You have to create a Touchable component in the Flatlist and add the navigation logic into the onPress logic of this Touchable Component.
this.props.navigation is a collection of data and methods, if you need to navigate through screens you need to use this.props.navigation.navigate('PlaceOrder')} instead, this is the method that does the navigation for you. You can console.log(this.props.navigation) to understand better.
Refer to how navigation is implemented in this answer https://stackoverflow.com/a/45407626/8851276
The code within FlatList of 'HomeScreen.js' is modified as follows
<FlatList
data={data}
renderItem={this.rendeList}
keyExtractor={item=>item.id}
/>
and the renderList function which was outside the class component is transferred inside and modified as
renderList = ({ item }) => {
return (
<TouchableOpacity
onPress={()=> this.props.navigation.navigate('PlaceOrder')}
activeOpacity={0.7}>
<NearbyLSP name={item.name}/>
</TouchableOpacity>
);
};
Due to transferring the renderList function inside the class component, it gets access to the this.props and it can be used for navigation to new screen.

React native search onChange input text, code structure

I am building a searchBar, whenever I do search I get undefined error because the value doesn't exist in state till I finish the whole value so I know that I will get error yet I am unable to solve it so I am trying to render cards according to the search input I think I did hard code my homeScreen I am not sure if I am doing it even right and here it comes the question to the three if statements inside render that I have is it good practice ? is it professional ? can i do something else which makes code easier to read and shorter ? I was thinking of eliminating the third if but I wasn't able to change state inside the second if so I had to add the toggle search function to let it work any ideas on how to eliminate the third if would be nice ..! thank you in advance guys
homeScreen.js
import axios from 'axios';
import React from 'react';
import {
ActivityIndicator,
ScrollView,
Text,
View,
TouchableOpacity,
TextInput,
} from 'react-native';
import Card from '../Components/Card/card';
export default class HomeScreen extends React.Component {
state = {
shows: [],
isLoading: true,
search: false,
title: '',
};
componentDidMount() {
this.getData();
}
toggleSearch = () => {
console.log('hlelleloe');
this.setState({
search: true,
});
};
getData = () => {
const requestUrls = Array.from({length: 9}).map(
(_, idx) => `http://api.tvmaze.com/shows/${idx + 1}`,
);
const handleResponse = data => {
this.setState({
isLoading: false,
shows: data,
});
};
const handleError = error => {
console.log(error);
this.setState({
isLoading: false,
});
};
Promise.all(requestUrls.map(url => axios.get(url)))
.then(handleResponse)
.catch(handleError);
};
render() {
const {isLoading, shows, search, title} = this.state;
if (isLoading) {
return <ActivityIndicator size="large" color="#0000ff" />;
} else if (!search) {
return (
<View>
<View>
<TouchableOpacity
onPress={this.toggleSearch}
style={{height: 300, width: 300}}>
<Text style={{textAlign: 'center', fontSize: 40}}>
Press to Search
</Text>
</TouchableOpacity>
</View>
<ScrollView style={{backgroundColor: '#E1E8E7'}}>
{shows.length &&
shows.map((show, index) => {
return (
<Card
key={show.data.id}
title={show.data.name}
rating={show.data.rating.average}
source={show.data.image.medium}
genres={show.data.genres}
language={show.data.language}
network={show.data.network}
schedule={show.data.schedule}
summary={show.data.summary}
navigation={this.props.navigation}
/>
);
})}
</ScrollView>
</View>
);
} else if (search) {
console.log(title);
return (
<View>
<TextInput
style={{
height: 100,
width: 100,
borderColor: 'gray',
borderWidth: 1,
}}
onChangeText={searchedTitle => (
<Card title={shows.data.searchedTitle} />
)}
/>
</View>
);
}
}
}
Card.js
import React from 'react';
import {
Image,
View,
Text,
Button,
StyleSheet,
TouchableOpacity,
} from 'react-native';
import {
widthPercentageToDP as wp,
heightPercentageToDP as hp,
} from 'react-native-responsive-screen';
import Icon from 'react-native-vector-icons/FontAwesome';
const Card = props => {
return (
<View style={styles.container}>
<Image style={styles.Image} source={{uri: `${props.source}`}} />
<Text style={styles.title}>{props.title}</Text>
<View style={styles.ratingContainer}>
<Text style={styles.rating}>Rating: {props.rating}</Text>
<Icon name="star" size={30} color="grey" />
</View>
<TouchableOpacity
style={styles.button}
onPress={() => {
props.navigation.navigate('Details', {
title: props.title,
rating: props.rating,
source: props.source,
genres: props.genres,
language: props.language,
network: props.network,
schedule: props.schedule,
summary: props.summary,
});
}}>
<Text style={styles.buttonText}>Press for details </Text>
</TouchableOpacity>
</View>
);
};
export default Card;
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
},
Image: {
flex: -1,
width: wp('90%'),
height: hp('65%'),
},
title: {
flex: 1,
fontSize: 40,
borderRadius: 10,
color: '#3C948B',
margin: 15,
justifyContent: 'center',
alignItems: 'center',
},
ratingContainer: {
flexDirection: 'row',
alignItems: 'center',
backgroundColor: 'white',
elevation: 6,
justifyContent: 'space-between',
borderWidth: 1,
width: 300,
},
rating: {
fontSize: 25,
paddingLeft: 15,
},
button: {
flex: 1,
color: '#3C948B',
backgroundColor: '#3C948B',
height: hp('7%'),
width: wp('70%'),
margin: 20,
alignItems: 'center',
borderBottomLeftRadius: 10,
borderTopRightRadius: 10,
},
buttonText: {
flex: 1,
fontSize: 25,
},
});
you Need to implement a constructor for your React component.
Typically, in React constructors are only used for two purposes:
Initializing local state by assigning an object to this.state
Binding event handler methods to an instance
Do
state = {
shows: [],
isLoading: true,
search: false,
title: '',
};
replace this with
constructor(props){
super(props);
this.state = {
shows: [],
isLoading: true,
search: false,
title: '',
};
}

How can I activate a button that is inside a NavigationOptions when I write in a text input in react navigation?

Very good, I am new to react native and I am trying to make a survey application in which I want to add a "next" button to my header and that can only be clicked or rather activated when I enter a character in a text input that will be in the center of the screen, so far what I did was the button and go to another screen with NavigationOptions but I can not do the other thing if someone knows would help me a lot of thanks.
import React, { Component } from "react";
import {
View,
Text,
StyleSheet,
TouchableOpacity,
Image,
TextInput
} from "react-native";
class Screen1 extends Component {
static navigationOptions = ({ navigation }) => ({
headerTitle: (
<Image
source={require('../Icons/icon3.png')}
style={{ width: 35, height: 35, marginLeft: 10 }}
/>
),
headerRight: (
<View>
<TouchableOpacity
disabled={Idonotknowhowtodoit}
onPress={() => navigation.navigate('Screen2')}
style={styles.Btn}>
<Text
style={styles.TxtBtn}>Next</Text>
</TouchableOpacity>
</View>
),
});
render() {
return (
<View style={styles.container}>
<TextInput
type="text"
placeholder="Enter Text"
/>
</View>
);
}
}
export default Screen1;
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center'
},
Btn: {
marginRight: 5,
justifyContent: 'center',
borderWidth: 2,
borderColor: '#000',
borderRadius: 2,
backgroundColor: '#000',
padding: 4
},
TxtBtn: {
textAlign: 'center',
color: '#fff',
fontSize: 14,
fontWeight: 'bold'
}
});
you can use params to check button is disable or active in navigationOptions.
class Screen1 extends Component {
constructor(props) {
super(props);
this.state = {
text: ''
};
}
static navigationOptions = ({ navigation }) => ({
headerTitle: (
<Image
source={require('../Icons/icon3.png')}
style={{ width: 35, height: 35, marginLeft: 10 }}
/>
),
headerRight: (
<View>
<TouchableOpacity
disabled={navigation.getParam('isDisable')} // get value from params and pass it to disabled props
onPress={() => navigation.navigate('Screen2')}
style={styles.Btn}>
<Text
style={styles.TxtBtn}>Next</Text>
</TouchableOpacity>
</View>
),
});
// set by a default value in componentDidMount to make the next button disable initially
componentDidMount() {
this.props.navigation.setParams({ isDisable: true });
}
render() {
return (
<View style={styles.container}>
<TextInput
type="text"
placeholder="Enter Text"
value={this.state.text} //set value from state
onChangeText={(text) => {
//when text length is greater than 0 than next button active otherwise it will be disable
let isDisable = text.length > 0 ? false : true
//set value in the state
this.setState({
text: text
})
// set value to params
this.props.navigation.setParams({ isDisable: isDisable});
}
}
/>
</View>
);
}
}

React Native: Navigation not working in component

I can not open the third page screen with the On Press method in the List Component.js file. I do not understand the problem. I want to know what I'm doing wrong with this. Where is the problem? Thank you. Please help.
Thank you for your reply but this will not work, my friend. I've updated the ListComponent.js file. The ListComponent.js file is actually a list. Please can you look again. ?
App.js
import React, { Component } from 'react';
import {
WebView,
AppRegistry, StyleSheet, Text, View, Button
} from 'react-native';
import { StackNavigator } from 'react-navigation';
import ListComponent from './ListComponent';
class App extends Component {
static navigationOptions =
{
title: 'App',
};
OpenSecondActivityFunction = () =>
{
this.props.navigation.navigate('Second');
}
render() {
return (
<View style={styles.container}>
<Button onPress = { this.OpenSecondActivityFunction } title = 'Open Second Activity'/>
</View>
);
}
}
class SecondActivity extends Component
{
static navigationOptions =
{
title: 'SecondActivity',
};
OpenThirdActivityFunction = () =>
{
this.props.navigation.navigate('Third');
}
render()
{
return(
<View style={{ flex: 1}}>
<ListComponents data={alapinCtrl} OpenThirdActivityFunction={this.OpenThirdActivityFunction} />
</View>
);
}
}
class ThirdActivity extends Component
{
static navigationOptions =
{
title: 'ThirdSecondActivity',
};
render()
{
return(
<View style={{ flex: 1}}>
<Text>3</Text>
</View>
);
}
}
export default ActivityProject = StackNavigator(
{
First: { screen: App },
Second: { screen: SecondActivity },
Third: { screen: ThirdActivity }
});
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},ActivityNameTextCss:
{
fontSize: 22,
color: 'black',
textAlign: 'center',
},
});
AppRegistry.registerComponent('ActivityProject', () => ActivityProject);
ListComponent.js
import React, { Component } from "react";
import {AppRegistry,View, Text, FlatList, ActivityIndicator} from "react-native";
import { List, ListItem, SearchBar } from "react-native-elements";
class ListComponents extends Component {
constructor(props) {
super(props);
this.state = {
loading: false,
data: [],
page: 1,
seed: 1,
error: null,
refreshing: false
};
}
renderSeparator = () => {
return (
<View
style={{
height: 1,
width: "98%",
backgroundColor: "#CED0CE",
marginLeft: "2%"
}}
/>
);
};
renderHeader = () => {
return <SearchBar placeholder="Type Here..." lightTheme round />;
};
renderFooter = () => {
if (!this.state.loading) return null;
return (
<View
style={{
paddingVertical: 20,
borderTopWidth: 1,
borderColor: "#CED0CE"
}}
>
<ActivityIndicator animating size="large" />
</View>
);
};
render() {
return (
<List containerStyle={{ borderTopWidth: 0, borderBottomWidth: 0 }}>
<FlatList
data={this.props.data}
renderItem={({ item }) => (
<ListItem
roundAvatar
title={`${item.name}`}
subtitle={item.coders}
containerStyle={{ borderBottomWidth: 0 }}
onPress={() => this.props.OpenThirdActivityFunction(item.coders)}
/>
)}
keyExtractor={item => item.coders}
ItemSeparatorComponent={this.renderSeparator}
ListHeaderComponent={this.renderHeader}
ListFooterComponent={this.renderFooter}
/>
</List>
);
}
}
export default ListComponents;
Only SecondActivity has the navigation prop; you need to explicitly pass it to child component:
<ListComponent navigation={this.props.navigation} />
Alternatively,
<ListComponent onPress={() => this.props.navigation.navigate('Third')} />;
Then just do <Button onPress={this.props.onPress} in your ListComponent.

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() : ''
}