Multiple Component with Same Function Name in React Native - javascript

I'm using react-native-material-menu's popup for showing menu options.
But the issue is, it's not working for multiple scenarios.
I mean when I click on first menu button, the same methods gets triggered and hence the same menu is opened every time.
What should be the better approach for to handle this particular scenario.
_menu = null;
setMenuRef = ref => {
this._menu = ref;
};
hideMenu = () => {
this._menu.hide();
};
showMenu = () => {
this._menu.show();
};
<FlatList
data={this.state.clientsList}
renderItem={({ item }) => {
return (
<View style={styles.caseItem} >
<Card style={styles.card}>
<CardItem>
<Body>
<View style={styles.rowTitle}>
<Text style={styles.title}>{item.FullName}</Text>
<Menu
ref={this.setMenuRef}
button={
<Icon
type="Feather"
name="more-vertical"
onPress={this.showMenu}
style{{fontSize:20,color:'#555'}}
/>
}>
<MenuItem onPress={this.hideMenu}>View</MenuItem>
<MenuItem onPress={this.hideMenu}>Edit</MenuItem>
<MenuItem onPress={this.hideMenu}>Delete </MenuItem>
</Menu>
</View>
<View>
<Text style={styles.lbl}>Email: <Text style={styles.lblValue}>{item.EmailID}</Text></Text>
<Text style={styles.lbl}>Client Type: <Text style={styles.lblValue}>{item.ClientType}</Text></Text>
</View>
</Body>
</CardItem>
</Card>
</View>
);
}}
keyExtractor={item => item.ID}
/>
Snack Here

To handle the states in the correct way, you will need to create a new Class which will be handling just the MenuItem
The below code will work: Here is the Snack.
import * as React from "react";
import { Text, View, StyleSheet } from "react-native";
import Constants from "expo-constants";
import { Container, Content, Card, CardItem, Body, Icon } from "native-base";
import * as Font from "expo-font";
import Menu, { MenuItem, MenuDivider } from "react-native-material-menu";
// You can import from local files
import AssetExample from "./components/AssetExample";
export default class App extends React.Component {
onView = () => {
alert("Do something here");
console.log("You can do what ever you want here");
}
render() {
return (
<View style={styles.container}>
<View style={styles.caseItem}>
<Card style={styles.card}>
<CardItem>
<Body>
<View style={styles.rowTitle}>
<Text style={styles.title}>John Doe</Text>
<CustomMenu onView={this.onView}/>
</View>
<View>
<Text style={styles.lbl}>
Email: <Text style={styles.lblValue}>john#yopmail.com</Text>
</Text>
<Text style={styles.lbl}>
Client Type: <Text style={styles.lblValue}>Individual</Text>
</Text>
</View>
</Body>
</CardItem>
</Card>
</View>
<View style={styles.caseItem}>
<Card style={styles.card}>
<CardItem>
<Body>
<View style={styles.rowTitle}>
<Text style={styles.title}>John Doe</Text>
<CustomMenu onView={this.onView}/>
</View>
<View>
<Text style={styles.lbl}>
Email: <Text style={styles.lblValue}>john#yopmail.com</Text>
</Text>
<Text style={styles.lbl}>
Client Type: <Text style={styles.lblValue}>Individual</Text>
</Text>
</View>
</Body>
</CardItem>
</Card>
</View>
<View style={styles.caseItem}>
<Card style={styles.card}>
<CardItem>
<Body>
<View style={styles.rowTitle}>
<Text style={styles.title}>John Doe</Text>
<CustomMenu onView={this.onView} />
</View>
<View>
<Text style={styles.lbl}>
Email: <Text style={styles.lblValue}>john#yopmail.com</Text>
</Text>
<Text style={styles.lbl}>
Client Type: <Text style={styles.lblValue}>Individual</Text>
</Text>
</View>
</Body>
</CardItem>
</Card>
</View>
</View>
);
}
}
class CustomMenu extends React.Component {
_menu = null;
setMenuRef = ref => {
this._menu = ref;
};
hideMenu = () => {
this._menu.hide();
};
onViewClick = () => {
const {onView} = this.props;
onView();
this.hideMenu();
}
showMenu = () => {
this._menu.show();
};
render() {
return (
<Menu
ref={this.setMenuRef}
button={
<Icon
type="Feather"
name="more-vertical"
onPress={this.showMenu}
style={{ fontSize: 20, color: "#555" }}
/>
}
>
<MenuItem onPress={this.onViewClick}>View</MenuItem>
<MenuItem onPress={this.hideMenu}>Edit</MenuItem>
<MenuItem onPress={this.hideMenu}>Delete </MenuItem>
</Menu>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: "center",
paddingTop: Constants.statusBarHeight,
backgroundColor: "#ecf0f1",
padding: 8
},
rowTitle: {
flexDirection: "row",
justifyContent: "space-between",
width: "100%"
},
title: {
fontSize: 14,
marginBottom: 5
},
lbl: {
fontSize: 11,
color: "#000"
},
lblValue: {
fontSize: 11,
color: "#555",
fontWeight: "normal"
},
caseItem: {
marginBottom: 0
}
});

Since the FlatList will iterate over the menu items, you need to maintain index for each iterable menu options.
You can check, you are passing item object within renderItems prop. So you can use the same item.id as a key to your child (iterable) component.
Since the child component now maintains an index, so now you can pass it in methods which will help you differentiate the event which got triggered from the child element.
I hope this might give you an idea about the issue.

Related

How to change change and track boolean state of each element in an array

Im trying to make it so that when a TouchableHighlight is pressed in react native it goes from one colour to another. I have state that is set to false and changes when the button is pressed. However this changes the colour for all elements in the map rather than for each item individually. Is there a way to update the state for each element independently?
Here is my code:
function OnboardingVibes() {
const [pressed, setPressed] = useState(false);
return (
<View style={{marginTop: 40}}>
<Text style={{fontSize: 22, color: '#FFF', marginBottom: 16}}>Vibes</Text>
<View style={styles.buttonContainer}>
{vibes.map((vibe) => {
return (
<TouchableHighlight onPress={() => setPressed(true)} style={{backgroundColor: pressed ? '#DDD' : '#4D2C5B', margin: 4, borderRadius: 4}} key={`vibe-${vibe}`}>
<Text style={styles.vibeButton}>{vibe}</Text>
</TouchableHighlight>
)
})}
</View>
</View>
);
}
One way to do it is to move the state down so you could have individual states. When the state is true at the top-level, all child components will receive the same state.
function TouchableVibe({vibe}) {
const [pressed, setPressed] = useState(false);
return (
<TouchableHighlight
onPress={() => setPressed(true)}
style={{
backgroundColor: pressed ? "#DDD" : "#4D2C5B",
margin: 4,
borderRadius: 4,
}}
>
<Text style={styles.vibeButton}>{vibe}</Text>
</TouchableHighlight>
);
}
function OnboardingVibes() {
return (
<View style={{ marginTop: 40 }}>
<Text style={{ fontSize: 22, color: "#FFF", marginBottom: 16 }}>
Vibes
</Text>
<View style={styles.buttonContainer}>
{vibes.map((vibe) => (
<TouchableVibe key={`vibe-${vibe}`} vibe={vibe} />
))}
</View>
</View>
);
}
const [pressed, setPressed] = useState(vibes.map(e=>false));
{vibes.map((vibe, index) => {
return (
<TouchableHighlight onPress={() => {
let new_Pressed =vibes.map(e=>false)
new_pressed[index]=true;
setPressed(new_pressed);
}}
style={{backgroundColor: pressed[index]
? '#DDD'
: '#4D2C5B',
margin: 4, borderRadius: 4}} key={`vibe-${vibe}`}>
<Text style={styles.vibeButton}>{vibe}</Text>
</TouchableHighlight>
)
})}
Instead of passing in a boolean to the state, pass in the id/vibe instead, and then in your conditional logic within the map you can determine if that single item is the same as the one you've clicked.
function OnboardingVibes() {
const [pressed, setPressed] = useState('');
return (
<View style={{marginTop: 40}}>
<Text style={{fontSize: 22, color: '#FFF', marginBottom: 16}}>Vibes</Text>
<View style={styles.buttonContainer}>
{vibes.map((vibe) => {
return (
<TouchableHighlight onPress={() => setPressed(vibe)} style={{backgroundColor: pressed === vibe ? '#DDD' : '#4D2C5B', margin: 4, borderRadius: 4}} key={`vibe-${vibe}`}>
<Text style={styles.vibeButton}>{vibe}</Text>
</TouchableHighlight>
)
})}
</View>
</View>
);
}

React Native TextInput Value persist when Tab is changed

I have encountered a weird issue in the newest react native where the value in the text input in a component remains when a tab is switched.
I can't figure what is going on and I thought it should re-render when tab is changed but it doesn't.
Here's my code
app.js
export default function App() {
const [tab, setTab] = useState("TAB1")
return (
<View style={styles.container}>
<View style={{ flexDirection: 'row' }}>
<TouchableOpacity
style={{ borderRadius: 5, borderWidth: 1, marginRight: 5, padding: 20 }}
onPress={() => setTab("TAB1")}
>
<Text>Tab 1</Text>
</TouchableOpacity>
<TouchableOpacity
style={{ borderRadius: 5, borderWidth: 1, padding: 20}}
onPress={() => setTab("TAB2")}
>
<Text>Tab 2</Text>
</TouchableOpacity>
</View>
<View style={{ marginTop: 20}}>
{
tab === "TAB1" ? (
<View>
<InputComponent tab={tab} />
</View>
) : (
<View>
<InputComponent tab={tab} />
</View>
)
}
</View>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
marginTop: 100,
padding: 10
},
});
inputcomponent.js
export function InputComponent({ tab }) {
const [value, setValue] = useState("");
return (
<View>
<Text>{tab}</Text>
<TextInput placeholder="INPUT HERE" value={value} onChangeText={setValue}/>
</View>
)
}
It seems like the input component re-renders but the text input within it doesn't change.
Demo Issue
This is such a good question. This is because we are importing it once and passing it to two different components. It changes the tab but uses the same textinput state because they are using the same key.
To fix this pass in the key prop so React knows that tab changed:
{
tab === "TAB1" ? (
<View>
<InputComponent key={1} tab={tab} />
</View>
) : (
<View>
<InputComponent key={2} tab={tab} />
</View>
)
}
Snack: https://snack.expo.io/mVVLb9uId
Read about keys: https://reactjs.org/docs/lists-and-keys.html#keys

tab navigation in react native is not working

import React from 'react'
import { View, Text, StyleSheet, TouchableWithoutFeedback } from "react-native";
const Header = props => {
const {
navigationState,
navigation,
activeTintColor,
inactiveTintColor
} = props;
const activeTabIndex = navigation.state.index;
return (
<View style={styles.containerHeader}>
<View style={styles.textContainer}>
<Text style={styles.textWhite}>Win Bets</Text>
<Text style={styles.textWhiteSmall}>win cash daily</Text>
</View>
<View style={styles.tabContainer}>
{navigationState.routes.map((route, index) =>{
const isRouteActive = index === activeTabIndex;
const tintColor = isRouteActive ? activeTintColor : inactiveTintColor;
return (
<TouchableWithoutFeedback
onPress={() => navigation.navigate(route.routeName)}
key={route.routeName}
>
<View>
<Text
style={{
fontSize: 17,
textTransform: "capitalize",
color: `${tintColor}`,
fontWeight: `${isRouteActive ? "700" : "normal"}`
}}
>
{route.routeName}
</Text>
</View>
</TouchableWithoutFeedback>
);
})}
</View>
</View>
);
};
the tabs show as they are supposed to be, but they are not clickable and cant navigate to a different page... also the home page doesnt show.
what could be the problems, or how do i configure the routes and routeName?

Is there a way to change text colour of a text element on a parent component from the child component in React Native?

Is there a way to change the text colour of a text element on a parent component from the child component in React Native?
I have done it with the icon in the code below, can I do it with text?
Discover More Parent Component
import React from 'react';
import {Text, View, TouchableOpacity, StyleSheet} from 'react-native';
import Colors from '#miniclementine:common/appearance/Colors';
import ClementineIcon from '#miniclementine:common/components/ClementineIcon';
export default function DiscoverMore({title, onPress, color}) {
return (
<TouchableOpacity onPress={onPress}>
<View style={styles.btnContainerStyle}>
<ClementineIcon name="add-circle-bold" color={color} size={24} />
<Text style={styles.btnTextStyle}> {title} </Text>
</View>
</TouchableOpacity>
);
}
const styles = StyleSheet.create({
btnContainerStyle: {
paddingVertical: 16,
flexDirection: "row",
justifyContent: "center",
alignItems: "center",
},
btnTextStyle: {
color: Colors.cornflower,
fontSize: 16,
textAlign: 'center',
}
});
HomePage bringing in DiscoverMore component and changing colour of icon using props
export default function SessionHomepage({navigation, passData, onPress}) {
const onPressSectionHeader = ({onPress}) => {
navigation.navigate(SESSIONS_LIST_PAGE, passData)
// #TODO: navigate to a page using navigation.navigate(pageName, params);
};
const onPressSectionHeaderInvalid = () => {
alert('Out of scope for this task')
// #TODO: navigate to a page using navigation.navigate(pageName, params);
};
const onContactPress = () => {
Linking.openURL('mailto:alina#clementineapp.co.uk?subject=SendMail&body=Description')
}
return (
<SafeAreaView style={styles.container}>
<ScrollView>
<View style={styles.content}>
<SectionHeader
title="Morning Sessions"
onPress={onPressSectionHeaderInvalid}
/>
<DiscoverMore
title="Discover more sessions"
onPress={onPressSectionHeaderInvalid}
color={Colors.cornflower}
/>
<SectionHeader
title="Pick-me-ups"
onPress={onPressSectionHeader}
/>
<DiscoverMore
title="Discover more sessions"
onPress={onPressSectionHeader}
color={Colors.cornflower}
/>
</View>
<View style={styles.sleepSection}>
<Image
source={require('../../assets/illustrations/sleep_section.png')}
style={{height: 250, width: '100%'}}
/>
</View>
<View style={styles.content}>
<View style={styles.sleepSection}>
<SectionHeader
title="Sleep Sessions"
onPress={onPressSectionHeaderInvalid}
/>
<Text>test</Text>
<DiscoverMore
title="Discover more sessions"
onPress={onPressSectionHeaderInvalid}
color={Colors.powderBlue}
/>
</View>
</View>
<BlueButton title="Leave us feedback" onPress={onContactPress}/>
</ScrollView>
</SafeAreaView>
);
}
Try this way
Discover page
export default function DiscoverMore({title, onPress, color, textColor}) {
return (
<TouchableOpacity onPress={onPress}>
<View style={styles.btnContainerStyle}>
<ClementineIcon name="add-circle-bold" color={color} size={24} />
<Text style={[styles.btnTextStyle,{color: textColor}]}> {title} </Text>
</View>
</TouchableOpacity>
);
}
Home Page
<DiscoverMore
title="Discover more sessions"
onPress={onPressSectionHeaderInvalid}
color={Colors.cornflower}
textColor={Colors.cornflower} // add text color here
/>

Trying to create two columns in react-native

I am trying to create a two column grid using flex. One column will be used to display a word or phrase and the second column will be used to translate the first column. Here is a link: http://imgur.com/nZGo8pb to give you a visual idea on what I am trying to achieve.
I am unable to get two columns side by side. I am only able to have my words appear on top of each other. This is best attempt. A huge failure.http://imgur.com/a/ICApr
My code is:
nent} from 'react';
import { Text, View,StyleSheet,Image,TextInput,ListView} from 'react-native';
class AddWords extends Component{
state = {words:['iku','konkai','kaikan'],
word:'',
EnglishWords:['go','this time','next time']
}
renderList(tasks){
return(
tasks.map((task) =>{
return(
<View key={task} style={styles.item}>
<Text>
{task}
</Text>
<Text>
</Text>
</View>
)
})
)
}
renderEnglishWords(english){
return(
english.map((english) =>{
return(
<View key={english} style={styles.item2}>
<Text>
{english}
</Text>
<Text>
</Text>
</View>
)
})
)
}
addWord(){
let words = this.state.words.concat([this.state.word]);
this.setState({words})
}
render(){
return(
<View style={styles.container}>
<TextInput
style={styles.input}
onChangeText={(word) => {
this.setState({word})
}}
onEndEditing={()=>this.addWord()}
/>
<View style={styles.wordContainer}>
{this.renderList(this.state.words)}
{this.renderEnglishWords(this.state.EnglishWords)}
<View style={styles.item2}>
</View>
</View>
</View>
)
}
}
const styles = StyleSheet.create({
container:{
flex:1,
borderWidth:3,
borderColor:'green',
flexDirection:'column',
paddingTop:10
},
wordContainer:{
flexDirection: 'column',
borderColor:'blue',
borderWidth:2
},
input:{
height:60,
borderWidth:1,
borderRadius:5,
borderColor:'black',
textAlign:'center',
margin:10,
paddingTop:20,
paddingBottom:10
},
item:{
borderColor:'red',
borderWidth:2
},
item2:{
borderColor:'black',
borderWidth:2,
flexDirection:'column',
}
})
export default AddWords;
Any help will be greatly appreciated.
Thanks.
You need to wrap both inner containers in another <View> with the following style:
<View style={styles.container}>
<TextInput
style={styles.input}
onChangeText={(word) => {
this.setState({ word })
}}
onEndEditing={this.addWord}
/>
<View style={{ flexDirection: 'row', flex: 1 }}>
<View style={styles.wordContainer}>
...
</View>
<View style={styles.item2}>
...
</View>
</View>
</View>
It's because the flexDirection in styles.wordContainer is set to 'column', it should be set to 'row'.
Check out this link on flex-direction examples.

Categories

Resources