Component Exception : undefined is not an object ( evaluating 'props.textInputId') - reactjs

I've two functional components CustomerDetails and other is MainDetails i want the state value of textInputName and textInputId, so i pass props in MainDetails component
But im getting error :
const CustomerDetails = ({ navigation }) => {
const [textInputName, setTextInputName] = React.useState("");
const [textInputId, setTextInputId] = React.useState("");
return (
<View style={styles.container}>
<NavigationBar
componentCenter = { () => <ComponentCenter /> }
navigationBarStyle= {{ backgroundColor:'#0095ff87' }}
statusBarStyle = {{ barStyle: 'light-content', backgroundColor: '#215e79' }}
/>
<Text style = { styles.heading }>Customer Details</Text>
{ error ?
<Text style={{color: 'red', textAlign:'center'}}>{error}
</Text> : null
}
<TextInput
style = {styles.input}
placeholder ="Enter Doctor Id"
onChangeText ={
(value) => setTextInputId(value)
}
value = { textInputId }
underlineColorAndroid="transparent"
/>
<TextInput
style = {styles.input}
placeholder = "Enter Name"
onChangeText = {
(value) => setTextInputName(value)
}
value = { textInputName }
underlineColorAndroid="transparent"
/>
<TextInput
style = {styles.input}
placeholder = "Enter Email(optional)"
underlineColorAndroid="transparent"
/>
<View style={{ marginTop: 15 }}>
<TouchableOpacity
style = {styles.submitButton}
onPress={() => {
if (textInputId.trim() === "") {
setError("Id Required.")
}
else if( textInputName.trim() === ""){
setError("Name Required.")
}
else{
navigation.navigate('MainDetails')}}
}>
<Text style = {styles.submitButtonText}> Connect </Text>
</TouchableOpacity>
</View>
</View>
)}
const MainDetails = ({ navigation, props }) => {
var idData = props.textInputId
var nameData = props.textInputName
return (
)}
function Customer() {
return (
<NavigationContainer>
<Stack.Navigator screenOptions={{ headerShown: false }}>
<Stack.Screen name="CustomerDetails" component={CustomerDetails} />
<Stack.Screen name="MainDetails" component={MainDetails} />
</Stack.Navigator>
</NavigationContainer>
);
}

You need to pass params to MainDetails via route (assuming you're using React Navigation): https://reactnavigation.org/docs/params/
So your code should look like:
const CustomerDetails = ({ navigation }) => {
const [textInputName, setTextInputName] = React.useState("");
const [textInputId, setTextInputId] = React.useState("");
...
navigation.navigate('MainDetails', { textInputId, textInputName })
...
const MainDetails = ({ navigation, route }) => {
const { textInputId, textInputName } = route.params;
return (
)
}

Related

How to prevent component from re-rendering , react native

So into the cartscreen, I am importing a dropdown component.
Now, there is a different dropdown inside the cartscreen called renderDropdown, and I'm using it to display two different data lists as two dropdowns for the user to choose from. Although this renderDropdown is functioning properly, selecting an imported dropdown causes the selected value in my renderDropdown to be cleared from the dropdown bar.
I verified that renderDropdown's value remains in the current state and I noticed that when I click on this imported drop-down, I get a message printed to the console(I have added console.log in cart screen whenever screen renders), which indicates that my entire screen is rendering. Could someone please look into this and let me know what's wrong?
Here is the code for a dropdown menu.
[I've also used the same component in a lot of other screens, and it works just fine]
import { StyleSheet, View } from 'react-native'
import React from 'react'
import { Dropdown} from 'react-native-element-dropdown'
import { COLORS } from '../constants'
const DropdownComponent = ({text, extractorValue, external_Id, data:mainData, extraData, value, setValue, isFocus, setIsFocus,area=false, retailer=false, retailerC=false, extraStyling}) => {
return (
<View style={[styles.container, {...extraStyling}]}>
<Dropdown
style={[styles.dropdown, isFocus && { borderColor: COLORS.blue90 }]}
placeholderStyle={styles.placeholderStyle}
selectedTextStyle={styles.selectedTextStyle}
inputSearchStyle={styles.inputSearchStyle}
iconStyle={styles.iconStyle}
data={mainData}
containerStyle={{
backgroundColor:COLORS.white10,
borderRadius:10,
marginTop:10,
borderColor:COLORS.gray10,
shadowColor:COLORS.blue90,
shadowOffset:{
height:15,
width: 15
},
elevation:19,
}}
search
maxHeight={300}
labelField={extractorValue ? extractorValue :"name"}
valueField={external_Id ? external_Id :"name"}
placeholder={!isFocus ? `Select ${text ? text : 'Item'}` : '...'}
searchPlaceholder="Search..."
value={value}
onFocus={() => setIsFocus(true)}
onBlur={() => setIsFocus(false)}
onChange={item => {
retailer ?
retailerC ?
(
setValue({...extraData, retailerClass:external_Id ? item[external_Id] : item.name})
):
(
( area ?
setValue({...extraData, area:external_Id ? item[external_Id] : item.name})
:
setValue({...extraData, route:external_Id ? item[external_Id] : item.name})
)
)
:
(
setValue(external_Id ? item[external_Id] : item.name)
)
setIsFocus(false)
}}
/>
</View>
)
}
export default DropdownComponent
and here is the cartScreen code which is causing trouble , also i want to include that i have to show 3 dropdowns on the screen so i was using the same component renderDropdown for all of them but i was having the same issue so i though it is because of third dropwdown (namely value scheme ) so i used external one but having the same issue.
const CartDetailsScreen = ({navigation}) => {
const [loading, setLoading] = React.useState(false)
const {cartItems, setCartItems } = useContext(mapContext)
const { createOrders, createOrderStatus, distributors, setDistributors, getDistributors, schemes, setSchemes, getSchemes} = useContext(commonUrlsContext)
useLayoutEffect(()=>{
navigation.setOptions({
headerShown:true,
headerTitle:'CART DETAILS',
...HeaderStyles,
headerLeft: () =>(
<HeaderLeft navigation={navigation} />
)
})
},[])
const [valueSchemeFromDropDown, setValueSchemeFromDropDown] = React.useState('')
React.useEffect(()=>{
getSchemes()
getDistributors()
return () =>{
setDistributors([])
setSchemes([])
}
},[])
React.useEffect(()=>{
if(createOrderStatus){
ToastAndroid.show('Order created successfully', ToastAndroid.CENTER)
navigation.dispatch(
CommonActions.reset({
index: 0,
routes: [
{ name: 'OrderSuccessful' },
],
})
);
setTimeout(()=>{{navigation.navigate('OrderSuccessful'), setCartItems([])}},1000)
}
},[createOrderStatus])
function RenderDropDown({index, text, data, external_Id, extractorValue, width, qtyScheme, valueScheme}){
return(
<View style={[styles.container, width && {width: width}]}>
<Dropdown
style={[styles.dropdown]}
placeholderStyle={styles.placeholderStyle}
selectedTextStyle={styles.selectedTextStyle}
inputSearchStyle={styles.inputSearchStyle}
iconStyle={styles.iconStyle}
data={data}
containerStyle={{
borderRadius:10,
marginTop:10,
borderColor:COLORS.gray10,
shadowColor:COLORS.gray90,
shadowOffset:{
height:10,
width: 10
},
elevation:15,
}}
search
maxHeight={300}
labelField={extractorValue ? extractorValue :"name"}
valueField={external_Id ? external_Id :"name"}
placeholder={`Select ${text ? text : 'item'}`}
searchPlaceholder="Search..."
// value={qtyScheme ? cartItems[index]?.distributorName}
onChange={item => {
qtyScheme ?
(
console.log(1),
cartItems[index].qtyScheme = item[external_Id],
setCartItems(cartItems)
)
:
(
console.log(2),
cartItems[index].distributorName = item[extractorValue],
cartItems[index].distributorId = item[external_Id],
setCartItems(cartItems)
)
}}
/>
</View>
)
}
const removeFromCart = (id) =>{
let updatedCart = cartItems.filter(product => product?.products?.Id !== id)
setCartItems(updatedCart)
ToastAndroid.show('Item Removed From the Cart.', ToastAndroid.SHORT)
if(updatedCart.length === 0){
navigation.goBack()
}
}
const submitHandler = () =>{
setLoading(true)
createOrders(cartItems, valueSchemeFromDropDown)
}
const reduceHandler = (item, index) =>{
if(item.qty === 1){
let newResults = cartItems.filter(product => product.products.Id !== item.products.Id)
setCartItems(newResults)
if(newResults.length === 0){
navigation.goBack()
}
}
else{
item.qty = item.qty - 1
let newResults = cartItems.filter(product => product.products.Id !== item.products.Id ? product : item)
setCartItems(newResults)
}
}
const [testValue, setTestValue] = React.useState('')
const [testFocus, setTestFocus] = React.useState(false)
const incrementHandler = (item, index) =>{
let newResults = cartItems.map(product =>
product.products.Id === item.products.Id ? {...item, qty: product.qty + 1} : product)
setCartItems(newResults)
}
return (
<>
{ loading ?
<RowCenter style={{flex:1, flexDirection: 'column', alignItems:'center'}}>
<StyledText fontSize={'20px'}>{createOrderStatus ? 'Redirecting..' : 'Loading..'}</StyledText>
</RowCenter> :
<>
<View style={{flex:0.8}}>
<ScrollView showsVerticalScrollIndicator={false}>
{cartItems?.map((item, index)=>{
return(
<View key={item?.products?.Id} style={{flexDirection:'row', padding:13, borderRadius:10, margin:10, alignItems:'center', backgroundColor:COLORS.green20, ...customShadow }}>
<View style={{width:'100%'}}>
<StyledText>Product Name : {item?.products?.Name}</StyledText>
<ExtraSpace />
{item?.products?.Description &&
<StyledText>Product Description : {item?.products?.Description?.length > 30 ? item?.products?.Description?.slice(0,30) + '...': item?.products?.Description }</StyledText>
}
<ExtraSpace />
<View style={{flexDirection:'row', justifyContent:'space-between', alignItems:'center', padding:10}}>
<TouchableOpacity onPress={()=>removeFromCart(item?.products?.Id)} style={{flexDirection:'row', justifyContent:'center', alignItems:'center'}}>
<SimpleLineIcons name="trash" size={15} color={COLORS.error} />
<StyledText color={COLORS.error}>Remove</StyledText>
</TouchableOpacity>
<View style={{width:'40%', flexDirection:'row', justifyContent:'space-between', alignItems:'center',}}>
<TouchableOpacity onPress={()=>reduceHandler(item, index)}>
<Feather name="minus-circle" size={22} color={COLORS.gray90} />
</TouchableOpacity>
<StyledText>Qty : {item?.qty}</StyledText>
<TouchableOpacity onPress={()=>incrementHandler(item, index)} >
<Feather name="plus-circle" size={22} color={COLORS.gray90} />
</TouchableOpacity>
</View>
</View>
<RenderDropDown index={index} text={'Distributor'} data={distributors?.response} external_Id='external_Id' extractorValue='name' />
{item.distributorName &&
<Label color={'#131821'}> Clone Selected : {item.distributorName}</Label>
}
<RenderDropDown id={item?.products?.id} index={index} text={'Quantity Scheme'} data={schemes?.response?.filter(scheme => scheme.Type__c === 'Quantity Scheme')} external_Id='Id' extractorValue='Name' qtyScheme={true}/>
</View>
</View>
)
})}
</ScrollView>
</View>
<View style={{flex:0.2, justifyContent:'center', alignItems:'center', borderWidth:1, borderTopLeftRadius:10, borderTopRightRadius:10, marginBottom:2, marginHorizontal:1, borderTopColor:'gray', backgroundColor:"transparent"}}>
<DropdownComponent text={'Value Scheme'} data={schemes?.response?.filter(scheme => scheme.Type__c === 'Value Scheme')} external_Id='Id' extractorValue={'Name'} value={testValue} setValue={setTestValue} isFocus={testFocus} setIsFocus={setTestFocus} extraStyling={{width: '100%', marginTop:10, marginBottom:1}}/>
{/* <RenderDropDown text={'Value Scheme'} data={valueScheme} external_Id='Id' extractorValue='Name' valueScheme={true}/> */}
<TextButton onPress={()=>{cartItems?.length === 0 ? null : submitHandler()}} componentStyling={{width:'94%', padding:8, marginHorizontal:10, marginBottom:10, marginTop:1}} title='Place Order'/>
</View>
</>
}
</>
)
}
Hey you have added these
onFocus={() => setIsFocus(true)}
onBlur={() => setIsFocus(false)}
and these are setStates , so it will re render when dropdwon opens or closes
Hope it helps. feel free for doubts

React context returning a null value despite default value

I keep getting the following error despite proving default values to my context.
TypeError: undefined is not an object (evaluating 'favoriteMealsCtx.ids')
Here's my context file:
import { createContext, useState } from "react";
export const FavoritesContext = createContext({
ids: [],
addFavorite: (id) => {
},
removeFavorite: (id) => {
}
});
function FavoritesContextProvider({children}) {
const [favoriteMealIds, setFavoriteMealIds] = useState([]);
function addFavorite(id) {
setFavoriteMealIds((currentFavIds) => [...currentFavIds, id]);
}
function removeFavorite(id) {
setFavoriteMealIds((currentFavIds) => currentFavIds.filter(mealId => mealId !== id))
}
const value = {
ids: favoriteMealIds,
addFavorite: addFavorite,
removeFavorite: removeFavorite
};
return (
<FavoritesContext.Provider value={value}>
{children}
</FavoritesContext.Provider>
)
}
export default FavoritesContextProvider;
Here's app.js using the <FavoritesContextProvider>:
export default function App() {
return (
<>
<StatusBar style="light"/>
<FavoritesContextProvider>
<NavigationContainer>
<Stack.Navigator
screenOptions={{
headerStyle: {backgroundColor: '#351401'},
headerTintColor: 'white',
contentStyle: {backgroundColor: '#3f2f25'},
}}
>
<Stack.Screen
name="Drawer"
component={DrawerNavigator}
options={{
headerShown: false,
}}
/>
<Stack.Screen name="MealsOverview" component={MealsOverviewScreen}/>
<Stack.Screen
name="MealDetail"
component={MealDetailScreen}
options={{
title: 'About the Meal',
}}
/>
</Stack.Navigator>
</NavigationContainer>
</FavoritesContextProvider>
</>
);
}
And finally here's the MealDetail screen throwing the error when it loads:
function MealDetailScreen({route, navigation}) {
const favoriteMealsCtx = useContext(FavoritesContext);
console.log(favoriteMealsCtx)
const mealId = route.params.mealId;
const mealIsFavorite = favoriteMealsCtx.ids.includes(mealId) // Here's the error
const selectedMeal = MEALS.find((meal) => meal.id === mealId);
function changeFavoriteStatusHandler() {
if ( mealIsFavorite ) {
favoriteMealsCtx.removeFavorite(mealId)
} else {
favoriteMealsCtx.addFavorite(mealId)
}
}
useLayoutEffect(() => {
navigation.setOptions({
headerRight: () => {
return (
<IconButton
icon={mealIsFavorite ? 'star' : 'star-outline'}
color="white"
onPress={changeFavoriteStatusHandler}
/>
);
},
});
}, [navigation, changeFavoriteStatusHandler]);
return (
<ScrollView style={styles.rootContainer}>
<Image style={styles.image} source={{uri: selectedMeal.imageUrl}}/>
<Text style={styles.title}>{selectedMeal.title}</Text>
<MealDetails
duration={selectedMeal.duration}
complexity={selectedMeal.complexity}
affordability={selectedMeal.affordability}
textStyle={styles.detailText}
/>
<View style={styles.listOuterContainer}>
<View style={styles.listContainer}>
<Subtitle>Ingredients</Subtitle>
<List data={selectedMeal.ingredients}/>
<Subtitle>Steps</Subtitle>
<List data={selectedMeal.steps}/>
</View>
</View>
</ScrollView>
);
}
I don't understand why const favoriteMealsCtx = useContext(FavoritesContext); is returning null when it should have been initialized already.

Exposing state props from functional component to navigationOptions function

I have a component that renders the input field, I want to pass the data to the next page when user clicks on "next" button in the header. What is the best practice for doing so? How do I expose this into Page.navigationOptions?
Or is it best to just set up redux for these types of things?
const Page = () => {
const [desc, getDesc] = useState('');
return (
<View style={styles.inputFieldDescContainer}>
<TextInput
multiline
placeholder='Write a description...'
onChangeText={(text) => getDesc(text)}
value={desc}
/>
</View>
);
};
// How do I pass desc properties down into navigationOptions?
Page.navigationOptions = (navData) => {
return {
headerTitle: 'Page,
headerRight: (
<HeaderButtons HeaderButtonComponent={HeaderButton}>
<Item
title='Next'
onPress={() => {
navData.navigation.navigate('NextPage', {data: navData});
}}
/>
</HeaderButtons>
),
headerBackTitle: null
};
};
/* NextPage.js */
const NextPage = (props) => {
console.log('enter props data', props.navigation.getParam('data'));
console.log('enter props navigation', props.navigation);
const [valueText, setValueText] = useState();
return (
<View>
<TextInput onChangeText={(text) => setValueText(text)} value={valueText}/>
<TouchableOpacity><Text>Create your workout</Text></TouchableOpacity>
</View>
);
;}
Sharing state and props between component and options is possible in React Navigation 5 https://blog.expo.io/announcing-react-navigation-5-0-bd9e5d45569e
In React Navigation 4, you can use params to store the value to be able to share it:
const Page = ({ navigation }) => {
const desc = navigation.getParam('description', '');
return (
<View style={styles.inputFieldDescContainer}>
<TextInput
multiline
placeholder='Write a description...'
onChangeText={(text) => navigation.setParams({ description: text )}
value={desc}
/>
</View>
);
}

React Native searchbar with react-navigation

I would like to add a searchbar in my header. I am using react-navigation, and want to create an effect like in the below 2 pictures. When you press the search icon, the hamburger icon becomes a arrow-back icon, the header title becomes a search field. I have tried to accomplish this with the navigationOptions but the problem is that it is static and it cannot be updated when an action happens on the header itself. So for experimenting what I want to accomplish is that when the search icon is pressed, the hamburger icon becomes a arrow-back icon. Thank you!
var search = false;
const menuButton = navData => (
<HeaderButtons HeaderButtonComponent={HeaderButton}>
<Item
title="Menu"
iconName="ios-menu"
onPress={() => {
navData.navigation.toggleDrawer();
}}
/>
</HeaderButtons>
);
const goBackButton = navData => (
<HeaderButtons HeaderButtonComponent={HeaderButton}>
<Item
title="Menu"
iconName="ios-arrow-back"
onPress={() => {
search=false
}}
/>
</HeaderButtons>
);
MyScreen.navigationOptions = navData => {
return {
headerTitle: 'My title',
headerLeft: search ? goBackButton : menuButton(navData),
headerRight: (
<BorderlessButton
onPress={() => search=true}
style={{ marginRight: 15 }}>
<Ionicons
name="md-search"
size={Platform.OS === 'ios' ? 22 : 25}
/>
</BorderlessButton>
)
}
};
try use state bro !
constructor(props) {
super(props);
this.state = {
search:false
}
}
const menuButton = navData => (
<HeaderButtons HeaderButtonComponent={HeaderButton}>
<Item
title="Menu"
iconName="ios-menu"
onPress={() => {
navData.navigation.toggleDrawer();
}}
/>
</HeaderButtons>
);
const goBackButton = navData => (
<HeaderButtons HeaderButtonComponent={HeaderButton}>
<Item
title="Menu"
iconName="ios-arrow-back"
onPress={() => {this.setState({search:false})}}
/>
</HeaderButtons>
);
MyScreen.navigationOptions = navData => {
return {
headerTitle: 'My title',
headerLeft: this.state.search ? goBackButton : menuButton(navData),
headerRight: (
<BorderlessButton
onPress={() => {this.setState({search:true})}}
style={{ marginRight: 15 }}>
<Ionicons
name="md-search"
size={Platform.OS === 'ios' ? 22 : 25}
/>
</BorderlessButton>
)
}
};
I have fixed it by passing information to my navigationOptions with setParams and getParam. The only problem I faced was in infinite loop which I could solve with the proper use of useEffect and useCallback.
const MyScreen = props => {
const dispatch = useDispatch();
var search = useSelector(state => state.verbs.search);
useEffect(() => {
props.navigation.setParams({search: search});
}, [search]);
const toggleSearchHandler = useCallback(() => {
dispatch(toggleSearch());
}, [dispatch]);
useEffect(() => {
props.navigation.setParams({toggleSearch: toggleSearchHandler});
}, [toggleSearchHandler]);
return (<View> ...
</View>
);
};
MyScreen.navigationOptions = navData => {
const toggleSearch = navData.navigation.getParam('toggleSearch')
return {
headerTitle: 'Verbs',
headerLeft: navData.navigation.getParam('search') ? gobackButton : menuButton(navData),
headerRight: (
<HeaderButtons HeaderButtonComponent={HeaderButton}>
<Item
title="Menu"
iconName="ios-star"
onPress={toggleSearch}
/>
</HeaderButtons>
)
}
};

react navigation custom tabBarComponent?

the navigationOptions code like that.
static navigationOptions = ({navigation})=>({
tabBarLabel:'查看',
headerTitle:navigation.state.params.title,
tabBarIcon: ({ tintColor,focused }) => (
<Image style={SKIN.tabImage} source={focused?AppImages.MyPost.lookchoose:AppImages.MyPost.look}/>
),
});
this is my Tab componet,how I can get tabBarLabel and tabBarIcon?
export default class Tab extends Component {
renderItem = (route, index) => {
const {
navigation,
jumpToIndex,
} = this.props;
const focused = index === navigation.state.index;
const color = focused ? this.props.activeTintColor : this.props.inactiveTintColor;
return (
<TouchableOpacity
key={index}
style={styles.tabItem}
onPress={() => jumpToIndex(index)}
>
<View
style={styles.tabItem}>
{this.props.renderIcon(color,focused)}
<Text style={{ color }}>{this.props.getLabel()}</Text>
</View>
</TouchableOpacity>
);
};
render(){
console.log('Tab this.props',this.props);
const {navigation,} = this.props;
const {routes,} = navigation.state;
return (
<View style={styles.tab}>
{routes && routes.map(this.renderItem)}
</View>
);
}
}
I custom Tab,now I want use that but some bug show me.
like that,
imagebug
please help me...
try updating the render method with this code:
render(){
console.log('Tab this.props',this.props);
const {navigation,} = this.props;
const {routes,} = navigation.state;
return (
<View style={styles.tab}>
//pass down the route and the index to the renderItem function
{routes && routes.map((route,index) => this.renderItem(route, index) )}
</View>
);
renderItem = (route, index) => {
const {
navigation,
jumpToIndex,
} = this.props;
const focused = index === navigation.state.index;
const color = focused ? this.props.activeTintColor : this.props.inactiveTintColor;
let TabScene = {
focused:focused,
route:route,
tintColor:color
};
return (
<TouchableOpacity
key={route.key}
style={styles.tabItem}
onPress={() => jumpToIndex(index)}
>
<View
style={styles.tabItem}>
{this.props.renderIcon(TabScene)}
<Text style={{ color }}>{this.props.getLabel(TabScene)}</Text>
</View>
</TouchableOpacity>
);
};

Resources