react navigation custom tabBarComponent? - reactjs

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>
);
};

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

How to change color of specific item in FlatList

const [itemsPressed, setItemsPressed] = useState(true);
const changeColor = (x) => {
setItemsPressed(x)
}
If your item has some unique identifying ID/name, you can use that to help you track which one is pressed. (You could also just store the full item itself and do object equality but just a simple id/string is more than enough)
const [pressedItemIds, setPressedItems] = useState({});
const changeColor = useCallback((item) => {
setPressedItemIds({...pressedItemIds, [item.id]: true});
}, []);
<FlatList data={data}
renderItem={({ item }) => (
<TouchableWithoutFeedback onPress={() => changeColor(item)}>
<View
style={backgroundColor: pressedItemIds.hasOwnProperty(item.id) ? '#8b0000': '#ffc6c4'}>
<Text>{data.text}</Text>
</View>
</TouchableWithoutFeedback>
)} />

Toggling Images with a boolean state of a react functional component

I have a react function component, that I do not want to convert to a class component. I want it to have the functionality so that when it is pressed, the image is changed. However, currently, it is only showing one of the images (checked.png). What am I doing wrong?
const SignUpScreen = () => {
const [check, setCheck] = useState(false);
const changeImage = () => {
setCheck((prevCheck) => !prevCheck);
};
return (
<View>
<Text>
Select the box to change the image
</Text>
<TouchableOpacity onPress={() => changeImage()}>
<Image
source={
{ check } === true
? require("../img/unchecked.png")
: require("../img/checked.png")
}
/>
</TouchableOpacity>
</View>
);
};
export default SignUpScreen;
Remove {} around the check
const SignUpScreen = () => {
const [check, setCheck] = useState(false);
const changeImage = () => {
setCheck((prevCheck) => !prevCheck);
};
return (
<View>
<Text>
Select the box to change the image
</Text>
<TouchableOpacity onPress={() => changeImage()}>
<Image
source={
check === true
? require("../img/unchecked.png")
: require("../img/checked.png")
}
/>
</TouchableOpacity>
</View>
);
};
export default SignUpScreen;
Otherwise it is an object created everytime

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

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

Iterating over children in stateless components react/react-native

I was working on a react-native application and I created a common component for display list items.
<View style={styles.container}>
<ItemsWithSeparator style={styles.itemsContainer}>
<AppRow />
<AppRow />
</ItemsWithSeparator>
</View>
Now, my ItemListSeparator is just iterates over the children and renders the list, so I thought I would make this a stateless component.
const ItemsWithSeparator = function ({children,style}) {
const childrenList = [];
const length = React.Children.count(children);
React.Children.forEach(
children,
(child,ii) => {
childrenList.push(child);
if (ii !== length -1) {
childrenList.push(
<View
key={`separator-${ii}`}
style={[styles.separator]}
/>
);
}
}
);
return (
<View style={style}>
{children}
</View>
);
};
But this throws an error saying 'React' not found.
However, it works fine with class based components. Following is the code which is working fine.
class ItemsWithSeparator extends React.Component {
render () {
const {children,style} = this.props;
const childrenList = [];
const length = React.Children.count(children);
React.Children.forEach(
children,
(child,ii) => {
childrenList.push(child);
if (ii !== length -1) {
childrenList.push(
<View
key={`separator-${ii}`}
style={[styles.separator]}
/>
);
}
}
);
return (
<View style={style}>
{children}
</View>
);
}
}
Can anyone help me understanding this? TIA!!
Update:
I was just trying few something and apparently got his to work:-
const ItemsWithSeparator = function ({children,style,...props}) {
const childrenList = [];
const length = React.Children.count(children);
React.Children.forEach(
children,
(child,ii) => {
childrenList.push(child);
if (ii !== length -1) {
childrenList.push(
<View
key={`separator-${ii}`}
style={[styles.separator]}
{...props}
/>
);
}
}
);
return (
<View style={style}>
{children}
</View>
);
};
But I am still a bit confused on how is this working. If someone could explain I would really be great.
Here is refactored version so you don't have to do this weird React.Children stuff :D Notice that you can return array when mapping children. There you can make if statements if needed.
const ItemsWithSeparator = ({children, style, ...props}) => {
const finalFields = children.map((child, index) => {
return [
child,
index !== children.length - 1 && (
<View key={index} {...props} style={styles.separator} />
)
];
});
return (
<View style={style}>
{finalFields}
</View>
);
};

Resources