I am trying to generate unique keys for some <TouchableOpacity> components that I would like to pass in onPress but it's not picking up the key variable.
const options = ["pic1","pic2","pic2"];
export default function ColourScreen() {
const handlePress = (key) => {
...do something wity key...
}
return (
<View>
<TouchableOpacity onPress={key => handlePress(key)}>
<Image
style={styles.icon}
source={require('../../../assets/icons/icon_1.jpg')}
/>
</TouchableOpacity>
<TouchableOpacity onPress={key => handlePress(key)}>
<Image
style={styles.icon}
source={require('../../../assets/icons/icon_2.jpg')}
/>
</TouchableOpacity>
<TouchableOpacity onPress={key => handlePress(key)}>
<Image
style={styles.icon}
source={require('../../../assets/icons/icon_3.jpg')}
/>
</TouchableOpacity>
</View>
)
}
I've tried manually setting it like:
<TouchableOpacity
key = {1}
onPress={key => handlePress(key)}>
<Image
style={styles.icon}
source={require('../../../assets/icons/icon_1.jpg')}
/>
</TouchableOpacity>
or:
{options.map((option, key) =>
<TouchableOpacity
key = {key}
onPress={() => handlePress(key)}>
<Image
style={styles.icon}
source={require('../../../assets/icons/icon_1.jpg')}
/>
</TouchableOpacity>, ...
)}
But this just creates multiple components.
How can I successfully do this?
You could take the key and map it to your icon.
const icons = [
require(`../../../assets/icons/icon_1.jpg`),
reqiuire(`../../../assets/icons/icon_2.jpg`),
require(`../../../assets/icons/icon_3.jpg`),
require(`../../../assets/icons/icon_4.jpg`),
]
{options.map((option, key) =>
<TouchableOpacity
key = {key}
onPress={() => handlePress(key)}>
<Image
style={styles.icon}
source={icons[key]}
/>
</TouchableOpacity>
)}
Sometimes when my data do not have unique id I use this approach with lodash
import { map, uniqueId } from 'lodash'
{map(someArray, () => (
<React.Fragment key={uniqueId()}>
<TextButton />
</React.Fragment>
))}
Just simply, do this.
<TouchableOpacity
onPress={() => yourFunctionName(1)}
>
your text or image goes here
<TouchableOpacity />
const yourFunctionName = (id) => {
console.log(id);
}
Related
I have created a TouchableOpacity which has an icon. The onPress method calls Image Picker.
<View style={styles.container}>
<TouchableOpacity style={styles.buttonStyle} onPress={pickImage}>
<MaterialIcons name="add-to-photos" size={24} color="black" />
</TouchableOpacity>
<TouchableOpacity style={styles.buttonStyle}>
<MaterialIcons name="add-to-photos" size={24} color="black" />
</TouchableOpacity>
</View>
Once the image is picked I am using useState to set the URI value to a variable Image1. Now I am want to display the Selected Image in the TouchableOpacity once the Image is picked.
How can I show selected image and not the icon once the image is picked.
You can do that using useState:
import {launchCamera, launchImageLibrary} from 'react-native-image-picker';
export const Avtar = () => {
const [image, setImage] = useState(null);
const pickImage = () => {
launchImageLibrary(options, async response => {
if (!response.didCancel) {
if (typeof onImageGet === 'function') {
setImage(response.assets[0].uri);
}
}
});
};
return (
<View style={styles.container}>
<TouchableOpacity style={styles.buttonStyle} onPress={pickImage}>
{!!image ? (
<Image
src={source}
style={[{width, height, borderRadius}, avtarStyle]}
resizeMode="cover"
/>
) : (
<MaterialIcons name="add-to-photos" size={24} color="black" />
)}
</TouchableOpacity>
<TouchableOpacity style={styles.buttonStyle}>
{!!image ? (
<Image
src={source}
style={[{width, height, borderRadius}, avtarStyle]}
resizeMode="cover"
/>
) : (
<MaterialIcons name="add-to-photos" size={24} color="black" />
)}
</TouchableOpacity>
</View>
);
};
Try with this:
const [imgPicker, setImgPicker] = useState('');
const cameraOpen = async () => {
const permisoCamara = await ImagePicker.requestCameraPermissionsAsync();
if (permisoCamara.status === "granted") {
const imgCamara = await ImagePicker.launchCameraAsync({
mediaTypes: ImagePicker.MediaTypeOptions.Images,
allowsEditing: false,
aspect: [4, 4],
quality: 0.1,
base64: true,
});
if (!imgCamara.cancelled) {
setImgPicker(imgCamara.base64);
}
} else {
alert("Permissions needed");
}
};
return (
<View>
<TouchableOpacity onPress={cameraOpen()}>
{imgPicker == '' ?
<MaterialIcons name="add-to-photos" size={24} color="black" /> :
<Image
source={{
uri: 'data:image/png;base64,' + imgPicker,
}}
/>
}
</TouchableOpacity>
</View>
);
The conditional that you need is inside the Touchable, just validate if the state have any value or not. Hope it helps
I have a screen with card views.
Each card view has:
1x picture
1x title
1x description
1x touchable opacity
I was hoping to figure out a way that each touchable opacity has different navigation.
Item 0 will have navigation to screen x, the item 1 will have navigation to screen y.
My doubt is it possible to have different functions for each touchable opacity ?
function ServiceCoverageButtong() {
const navigation = useNavigation();
return (
<View>
<TouchableOpacity
style={styles.button}
onPress={() => navigation.navigate('GeneralInformationSupportScreen')}>
<Text style={styles.buttonText}>Teste</Text>
</TouchableOpacity>
</View>
);
}
const CardItemNewsProvider = ({item, index}) => {
return (
<View style={styles.container} key={index}>
<Image source={item.imgUrl} style={styles.image} />
<Text style={styles.header}>{item.title}</Text>
<Text style={styles.body}>{item.body}</Text>
<ServiceCoverageButtong />
</View>
);
};
How can I create several functions and use the item of CardItemNewsProvider?
I am new to React Native and I am struggling with doing that.
Thanks :)
Yes it's possible. You can pass a prop to your <ServiceCoverageButtong state={"0"}/>
And in your ServiceCoverageButtong() get the state from your props and run a check on what should be returned.
function ServiceCoverageButtong({state}) {
const navigation = useNavigation();
if (state == "0") {
return (
<View>
<TouchableOpacity
style={styles.button}
onPress={() => navigation.navigate('GeneralInformationSupportScreen')}>
<Text style={styles.buttonText}>Teste</Text>
</TouchableOpacity>
</View>
);
}
} else {
return (
<View>
<TouchableOpacity
style={styles.button}
onPress={() => navigation.navigate('anotherScreen')}>
<Text style={styles.buttonText}>Teste</Text>
</TouchableOpacity>
</View>
);
}
}
If you use one component for your buttons, you can just add onPress prop to your DataNewsProvider
let DataNewsProvider = [
{
title: NewsHomeCountryTitle,
body: NewsHomeCountryBody,
imgUrl: Images.newsYourCountryImage,
textButton: NewsTextButton,
onPress: () => navigation.navigate('GeneralInformationSupportScreen'),
},
{
title: NewsWorldwideTitle,
body: NewsWorldwideBody,
imgUrl: Images.newsWorldwideImage,
textButton: NewsTextButton,
onPress: () => navigation.navigate('anotherScreen'),
},
];
And pass it to your button components TouchableOpacity
const CardItemNewsProvider = ({item, index}) => {
return (
<View style={styles.container} key={index}>
<Image source={item.imgUrl} style={styles.image} />
<Text style={styles.header}>{item.title}</Text>
<Text style={styles.body}>{item.body}</Text>
<ServiceCoverageButtong state={item.stateButton} onPress={item.onPress}/>
</View>
);
};
This way you don't need to have additional conditions, and you just pass those functions as it is.
Thanks caslawter!
For anyone interested.
function ServiceCoverageButtong({state}) {
const navigation = useNavigation();
if (state === '0') {
console.log('state', state);
return (
<View>
<TouchableOpacity
style={styles.button}
onPress={() =>
navigation.navigate('GeneralInformationSupportScreen')
}>
<Text style={styles.buttonText}>Hi I'm a test</Text>
</TouchableOpacity>
</View>
);
} else {
console.log('state', state);
return (
<View>
<TouchableOpacity
style={styles.button}
onPress={() => navigation.navigate('anotherScreen')}>
<Text style={styles.buttonText}>You're really into testing</Text>
</TouchableOpacity>
</View>
);
}
}
const CardItemNewsProvider = ({item, index}) => {
return (
<View style={styles.container} key={index}>
<Image source={item.imgUrl} style={styles.image} />
<Text style={styles.header}>{item.title}</Text>
<Text style={styles.body}>{item.body}</Text>
<ServiceCoverageButtong state={item.stateButton} />
</View>
);
};
And another snipet:
let DataNewsProvider = [
{
title: NewsHomeCountryTitle,
body: NewsHomeCountryBody,
imgUrl: Images.newsYourCountryImage,
textButton: NewsTextButton,
stateButton: '0',
},
{
title: NewsWorldwideTitle,
body: NewsWorldwideBody,
imgUrl: Images.newsWorldwideImage,
textButton: NewsTextButton,
stateButton: '1',
},
];
I am implementing my own Modal, trying to replace the Alert.alert with something more beautiful. I made it to be displayed when needed, but it is not hiding on the button press, but I think I transferred it the needed function. My modal structure is the following:
export const RCModal = ({ title, visible, onButtonPress }) => {
return (
<Modal
animationType='fade'
transparent={true}
visible={visible}
>
<View style={styles.container}>
<Text style={styles.title}>{title}</Text>
<Pressable style={styles.button} onPress={onButtonPress}>
<Text style={styles.text}>OK</Text>
</Pressable>
</View>
</Modal>
)
};
And it is used in the application in the following way:
// ...
const [alertVisible, setAlertVisible] = useState(false);
const [alertTitle, setAlertTitle] = useState();
const [alertOnPress, setAlertOnPress] = useState();
// ...
const winner = (theWinner) => {
setBlocked(true);
setAlertTitle(`${theWinner} win!`);
setAlertOnPress(() => setAlertVisible(!alertVisible));
setAlertVisible(true);
}
// ...
return (
<View style={styles.container}>
<RCModal title={alertTitle} visible={alertVisible} onButtonPress={alertOnPress} />
<ScrollView contentContainerStyle={{ flexGrow: 1, justifyContent: 'center' }}>
<Text style={styles.title}>Noughts and Crosses</Text>
<Text style={styles.tip}>Get {winCondition()} in row, column or diagonal</Text>
<View style={styles.buttonsContainer}>
<Text style={styles.turnContainer}>Turn: <Text style={[styles.turn, { color: turn === 'X' ? '#2E86C1' : '#E74C3C'}]}>{turn}</Text></Text>
<TouchableHighlight underlayColor="#000000" style={[styles.button, styles.newGameButton]} onPress={setInitialFieldState}>
<Text style={styles.buttonText}>New game</Text>
</TouchableHighlight>
</View>
<Field state={fieldState} size={fieldSize} onCellPress={onCellPress} />
</ScrollView>
<View style={styles.settingsButtonContainer}>
<TouchableHighlight underlayColor={theme.colors.secondary} style={styles.settingsButton} onPress={onSettingsPress}>
<Image source={require('../img/settings.png')} style={styles.settingsIcon} />
</TouchableHighlight>
</View>
</View>
);
};
When the winner() is called, it is displayed as it should, but when I press OK button, it is not hiding. How can I fix it?
You can use setAlertVisible to change the alertVisible state:
<RCModal title={alertTitle} visible={alertVisible} onButtonPress={() => setAlertVisible(false)} />
The answer was that to set a function like a state variable, I needed to set it like
setAlertOnPress(() => () => setAlertVisible(false))
(2 x () =>)
I am using flatlist for show user input notes in my app. But Flat List is not render any item that i entered. I used static list for testing and it works correctly. but when i enter a text through textinput it is not showing.
const addToDoHandler = () => {
if(userInput == ''){
return;
}
const newTodo = {
id: Math.floor(Math.random() * 1000),
text: userInput,
completed: false,
};
const newTodos = [...todos, newTodo];
setTodos(newTodos);
//clear user input
setUserInput('');
};
const renderItem = ( {item} ) => {
<View style={{backgroundColor:'#6b4d87', marginVertical: 10, flexDirection:'row', alignItems:'center', justifyContent:'space-between', height:60, width:'95%', borderRadius:10 }}>
<View>
<Text style={{color:'black', fontSize:20, fontWeight:'bold'}}>{item.text}</Text>
</View>
<TouchableOpacity onPress={() => deleteHandler(item.id)}>
<TrashIcon size={35} name="trash" style={{color:'#eff9f0'}} />
</TouchableOpacity>
</View>
};
return (
<SafeAreaView style={styles.root}>
<View style={styles.txtInputView}>
<TextInput
value={userInput}
onChangeText={text => setUserInput(text)}
style={styles.textInput}
placeholder={"Enter Your Note..."}
/>
<TouchableOpacity style={styles.addButton} onPress={addToDoHandler}>
<PlusSign size={35} name="add-sharp" style={styles.plusIcon}/>
</TouchableOpacity>
</View>
<View style={{alignItems:'center'}}>
<FlatList
data={todos}
renderItem={renderItem}
keyExtractor={item => item.id}
/>
</View>
</SafeAreaView>
);
You need to return the components from renderItem.
Change it to:
const renderItem = ( {item} ) => (
<View style={{backgroundColor:'#6b4d87', marginVertical: 10, flexDirection:'row', alignItems:'center', justifyContent:'space-between', height:60, width:'95%', borderRadius:10 }}>
<View>
<Text style={{color:'black', fontSize:20, fontWeight:'bold'}}>{item.text}</Text>
</View>
<TouchableOpacity onPress={() => deleteHandler(item.id)}>
<TrashIcon size={35} name="trash" style={{color:'#eff9f0'}} />
</TouchableOpacity>
</View>
);
I made 2 screens one home screen and 2nd edit screen I need to edit data of home screen from edit screen and save it and that data should also update in home and detail screen. How can I do this without redux or context. Can anyone tell me.
Home.js
class Home extends Component {
state = {
post: [
{
key: "1",
title: "A Good Boi",
des: "He's a good boi and every one know it.",
image: require("../assets/dog.jpg"),
},
],
};
render() {
return (
<FlatList
data={this.state.post}
renderItem={({ item }) => (
<>
<TouchableOpacity
activeOpacity={0.7}
onPress={() => this.props.navigation.navigate("Edit", item)}
style={styles.Edit}
>
<MaterialCommunityIcons
name="playlist-edit"
color="green"
size={35}
/>
</TouchableOpacity>
<Card
title={item.title}
subTitle={item.des}
image={item.image}
onPress={() => this.props.navigation.navigate("Details", item)}
/>
</>
)}
/>
Edit.js
class ListDetails extends Component {
render() {
const listing = this.props.route.params;
return (
<View>
<Image style={styles.image} source={listing.image} />
<View style={styles.detailContainer}>
<AppTextInput value={listing.title} />
<AppTextInput value={listing.des} />
</View>
<AppButton
text="Save"
onPress={() => this.props.navigation.goBack("Home")}
/>
</View>
Details.js
const listing = this.props.route.params;
return (
<View>
<Image style={styles.image} source={listing.image} />
<View style={styles.detailContainer}>
<Text style={styles.title}>{listing.title}</Text>
<Text style={styles.des}>{listing.des}</Text>
</View>
</View>
);
You can pass a function from your home screen to setState for it in your edit screen. In case navigating to edit screen causes the home screen unmounted, you can change the navigate method to push of stack navigator (I haven't tested yet). The code now should look like:
HomeScreen.js:
onEdit=(data)=>{
setState(...);
}
...
<TouchableOpacity
activeOpacity={0.7}
onPress={() => this.props.navigation.navigate("Edit", {item, onEdit})} //use push instead if error occured
style={styles.Edit}
>
...
Edit.js
class ListDetails extends Component {
render() {
const {item:listing, onEdit} = this.props.route.params;
return (
<View>
<Image style={styles.image} source={listing.image} />
<View style={styles.detailContainer}>
<AppTextInput value={listing.title} />
<AppTextInput value={listing.des} />
</View>
<AppButton
text="Save"
onPress={() => { onEdit(...); this.props.navigation.goBack("Home");}}
/>
</View>