My code fetches a json file, calculates a value and I want to pass this value into the style of TouchableOpacity. Below is my attempt:
const [height, setHeight] = useState(0)
const [isLoading, setLoader] = useState(true)
const fetching = async () => {
...//code that fetches the value
setHeight(value)
setLoader(false)
}
if (isLoading) {
return (
<Text> Loading...</Text>
)
}
return (
<View>
<TouchableOpacity
style={{height: height, width:30, backgroundColor: "red" }} />
... //other parts of the return statement
</View>
)
the complete code:
<View style={{height: height}}>
<TouchableOpacity
style={{width:30, borderWidth:5, marginTop:20, backgroundColor:"blue", height:height}}>
</TouchableOpacity>
</View>
Any help would be appreciated.
I think your useState is fine. However either the parent View doesn't have any space or the TouchableOpacity has nothing to display.
You can try to do:
return (
<View>
<TouchableOpacity
style={{height, width:30, borderWidth: 5 }} />
... //other parts of the return statement
</View>
)
If you see no border, then it's a problem with the parent View
You can then try:
return (
<View style={{flex: 1}}>
<TouchableOpacity
style={{height, width:30, borderWidth: 5 }} />
... //other parts of the return statement
</View>
)
You could also try adding a Text component with some text to the TouchableOpacity.
This code:
import React, { useEffect, useState } from 'react';
import {Text, View, TouchableOpacity} from 'react-native';
export default function App() {
const [height, setHeight] = useState(0)
const [isLoading, setLoader] = useState(true)
useEffect(() => {
const timerTask = setInterval(() => {
setHeight(Math.random() * 200)
setLoader(false);
}, 5000);
return () => clearInterval(timerTask);
}, [])
if (isLoading) {
return (
<Text> Loading...</Text>
)
}
return (
<View style={{flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<TouchableOpacity
style={{width: 30, borderWidth: 5, marginTop: 20, backgroundColor: "blue", height}}>
</TouchableOpacity>
</View>
)
}
Produces a blue touchable opacity that changes height every 5 seconds. Also when I touch it, it turns a lighter shade.
It seems to me you might not be passing the style property in the render function of TouchableOpacity, TouchableOpacity is your own custom component, isn't it?
const TouchableOpacity = ({ style }) => {
...
return (
<div style={style} >
...
</div>
);
}
Add a height and width to your element's style
height: 50,
width: '100%',
backgroundColor: "#000000"
Related
I am relatively new to React-Native and mobile app development. I am trying to create a clone of Instagram. And in the post screen what I do is I have a button Camera and once I click on it, that should take me to CameraView screen. And in the CameraView screen I will take a picture using expo-camera and save the picture in a variable. So here, I want to export the picture and then import it in my Post screen, so that the user can have a preview of what the picture looks like before uploading. And another thing to note is that, the Post screen will by default have a Placeholder image and once the image is taken the placeholder image has to be replaced with the image.
I tried this code but it didnt work.
My Post screen :-
import { View, Text, Image, TextInput } from 'react-native';
import React, { useState, useEffect } from 'react'
import * as Yup from 'yup';
import { Formik } from 'formik'
import { Button, Divider } from 'react-native-elements';
import { useNavigation } from '#react-navigation/native';
import CameraView, { takePicture } from '../../screens/CameraView';
const PLACEHOLDER_IMG = require('../../assets/defImg.png')
const uploadPostSchema = Yup.object().shape({
caption: Yup.string().max(2200, 'Caption cannot exceed character limit.')
})
const FormikPostUploader = ({route}) => {
const [thumbnailUrl, setThumbnailUrl] = useState(PLACEHOLDER_IMG)
const navigation = useNavigation()
const handleCameraPress = () => {
UserImage = takePicture()
setThumbnailUrl(UserImage)
navigation.navigate('CameraView')
}
return (
<Formik
initialValues={{ imageUrl: '', caption:'' }}
onSubmit={(values) => console.log(values)}
validationSchema={uploadPostSchema}
validateOnMount={true}>
{({
handleBlur,
handleChange,
handleSubmit,
values,
errors,
isvalid
}) => (
<>
<View style={{ margin: 20, justifyContent: 'space-between', flexDirection: 'row' }}>
<Image source={thumbnailUrl} />
<View style={{ flex: 1, margin: 15 }}>
<TextInput
placeholder='Write a caption...'
placeholderTextColor='gray'
multiline={true}
style={{ color: 'white', fontSize: 17 }}
onChangeText={handleChange('caption')}
onBlur={handleBlur('caption')}
value={values.caption}
/>
</View>
</View>
<Divider width={0.5} orientation='vertical'/>
<View style={{ alignItems: 'center', marginTop: 15 }}>
<Text style={{ fontSize: 18, fontWeight: 'bold', color: 'white' }}>Choose Image</Text>
</View>
<View style={{ justifyContent: 'space-between', flexDirection: 'column', margin: 20 }}>
<View style={{ justifyContent: 'space-between', flexDirection: 'row', margin: 30 }}>
<Button title='Camera' onPress={handleCameraPress}/>
<Button title='Gallery' onPress={() => navigation.navigate('GalleryView')} />
</View>
<Button onPress={handleSubmit} title='Post' disabled={isvalid} />
</View>
</>
)}
</Formik>
)
}
export default FormikPostUploader
And my CameraView screen :-
import { View, Text, Image, StyleSheet, Button } from 'react-native'
import React, {useState, useEffect} from 'react'
import { Camera } from 'expo-camera'
import { TouchableOpacity } from 'react-native'
import { useNavigation } from '#react-navigation/native'
import { PLACEHOLDER_IMG } from '../components/newPost/FormikPostUploader'
export const takePicture = async(camera) => {
const data = await camera.takePictureAsync(null)
return data.uri
}
const Header = () =>{
const navigation = useNavigation()
return (
<View style = {styles.headerContainer}>
<TouchableOpacity style = {{marginBottom:15}} onPress={() => navigation.goBack()}>
<Image source = {require('../assets/back.png')} style = {{width:35, height:35, margin:20}}/>
</TouchableOpacity>
<Text style={{color:'white', fontWeight: '700', fontSize:20, marginRight: 25, marginTop:35, marginBottom:15}}>New Post</Text>
<Text> </Text>
</View>
)
}
const CameraView = () => {
const UserImage = takePicture()
const navigation = useNavigation()
const [hasCameraPermission, setHasCameraPermission] = useState(null)
const [thumbnailUrl, setThumbnailUrl] = useState(PLACEHOLDER_IMG)
const [camera, setCamera] = useState(null)
const [type, setType] = useState(Camera.Constants.Type.back)
const captureImage = async () => {
if (camera) {
const UserImage = await takePicture(camera)
setThumbnailUrl(UserImage)
}
}
useEffect(() => {
(async() => {
const cameraStatus = await Camera.requestCameraPermissionsAsync()
setHasCameraPermission(cameraStatus.status === 'granted')})()},
[])
if (hasCameraPermission === null) {
return <View/>
}
if (hasCameraPermission === false){
return <Text>Enable access for Camera to proceed</Text>
}
return (
<View style={{ flex: 1, backgroundColor: 'black'}}>
<Header/>
<Camera
ref = {ref => setCamera(ref)}
style={styles.fixedRatio}
type={type}
ratio={'1:1'}/>
<View style = {styles.cameraContainer}>
<TouchableOpacity onPress={() => {setType(type === Camera.Constants.Type.back ? Camera.Constants.Type.front : Camera.Constants.Type.back)}}>
<Image source={require('../assets/turn-camera.png')} style = {{width:70, height:70, margin: 15}}/>
</TouchableOpacity>
<TouchableOpacity onPress={() => {captureImage(), navigation.goBack(), console.log(UserImage)}}>
<Image source={require('../assets/camera-shutter.png')} style = {{width:70, height:70, marginRight:60}}/>
</TouchableOpacity>
</View>
</View>
)
}
const styles = StyleSheet.create({
cameraContainer: {
flex:0.15,
backgroundColor: 'black',
flexDirection:'row',
justifyContent: 'space-between',
alignItems: 'center',
},
fixedRatio: {
flex:0.99,
aspectRatio:0.9
},
headerContainer: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
backgroundColor:'black'
},
icon: {
width:25,
height: 25,
marginHorizontal:15,
marginTop:30,
},
iconContainer: {
flexDirection:'row',
justifyContent: 'space-between',
height:50,
},
}
)
export default CameraView
Am I doing anything wrong. Can you help me fix this. Thanks in advance!
I am new to React Native
and I want to create elements and return them with a button onPress function, I don´t want to hide and show like a Modal, else create them.
import React from "react"
import { Button, StyleSheet, Text, View } from 'react-native';
function createElement() {
return(
<View style={styles.elementStyle}>
<Text style={styles.txt}>ELement</Text>
</View>
)
}
const App = () => {
return (
<View style={{ flex: 1,backgroundColor: '#fff', alignItems: 'center',justifyContent: 'center',}}>
<Button title="create element" onPress={() => createElement()}/>
</View>
);
}
export default App;
const styles = StyleSheet.create({
container: {flex: 1, backgroundColor: '#fff', alignItems: 'center', justifyContent: 'center',
},
elementStyle: { backgroundColor:'grey', width:'95%', height: 90, margin: 10, justifyContent: "center", borderRadius: 10, fontWeight: "bold" },
txt: {textAlign:'center',fontSize:28,color:'#fff',fontWeight: "bold"}});
I tried with functions that return components, but nothing works
Do you want to have multiple elements or just a single modal?
For multiple elements, do the below. For a single element, it's easiest to just use show / hide logic.
The best way to do this is have an array in state like so:
const [elementArray, setElementArray] = useState();
Your createElement method instead should become two parts, adding elements to the array with the content you want, which you can then render in the main return function with a map method.
const addElement = () => {
// Just using text here. If you want a more complex element, you will have to add things to the object.
const newElementText = 'Element';
const newElementArray = elementArray.slice();
newElementArray.push(newElementText);
setElementArray([...newElementArray]);
}
Then in your return function in the component:
{elementArray.map((element) => {
return (
<View style={styles.elementStyle}>
<Text style={styles.txt}>element</Text>
</View>
);
}
)}
Make sure you add a useEffect hook so the component rerenders when you add a new element:
useEffect(()=> {}, [elementArray])
You can't navigate to a component like that. If you are making it so your component appears on the click of a button I suggest building a Stack by importing react-native/navigation. Then, building your structure as shown. My explanation might not have been the best because your initial code was unstructured. This should give you an even better answer. docs
const navigation = useNavigation();
function createElement() {
return(
<View style={styles.elementStyle}>
<Text style={styles.txt}>Element</Text>
</View>
)
}
function Home() {
return (
<View style={{ flex: 1,backgroundColor: '#fff', alignItems: 'center',justifyContent: 'center',}}>
<Button title="create element" onPress={() => navigation.navigate("Element")}/>
</View>
);
}
const App = () => {
<Stack.Navigator screenOptions={{ headerShown: false }}>
<Stack.Screen name="Home" component={Home} />
<Stack.Screen name="Element" component={CreateElement} />
</Stack.Navigator>
}
Srry if the title makes no sense. Don't know a better title.
How can I save route.params items that I pass to my second screen using AsyncStorage?
In my first screen i have a bunch of data in a FlatList that can be opened with a Modal. Inside that Modal I have a TouchableOpacity that can send the data thats inside the Modal to my second screen. The data that has been passed to the second screen is passed to a FlatList. The data in the FlatList should be saved to AsyncStorage. Tried alot of things getting this to work, but only getting warning message
undefined. Code below is the most recent progress.
Using React Navigation V5.
FIRST SCREEN
const [masterDataSource, setMasterDataSource] = useState(DataBase);
const [details, setDetails] = useState('');
<TouchableOpacity
onPress={() => {
const updated = [...masterDataSource];
updated.find(
(item) => item.id === details.id,
).selected = true;
setMasterDataSource(updated);
navigation.navigate('cart', {
screen: 'cart',
params: {
items: updated.filter((item) => item.selected),
},
});
setModalVisible(false);
}}>
<Text>Add to cart</Text>
</TouchableOpacity>
SECOND SCREEN
import React, { useEffect, useState } from 'react';
import { View, Text, FlatList, TouchableOpacity } from 'react-native';
import { useTheme } from '../Data/ThemeContext';
import AsyncStorage from '#react-native-async-storage/async-storage';
import Ionicons from 'react-native-vector-icons/Ionicons';
export default function ShoppingList({ route, navigation }) {
const [shoppingList, setShoppingList] = useState([]);
const { colors } = useTheme();
const todo = () => {
alert('Todo');
};
useEffect(() => {
restoreShoppingListAsync();
}, []);
const shoppingListAsync = () => {
const shoppingList = route.params && route.params.items;
setShoppingList(list);
storeShoppingList(list);
};
const asyncStorageKey = '#ShoppingList';
const storeShoppingListAsync = (list) => {
const stringifiedList = JSON.stringify(list);
AsyncStorage.setItem(asyncStorageKey, stringifiedList).catch((err) => {
console.warn(err);
});
};
const restoreShoppingListAsync = () => {
AsyncStorage.getItem(asyncStorageKey)
.then((stringifiedList) => {
console.log(stringifiedList);
const parsedShoppingList = JSON.parse(stringifiedList);
if (!parsedShoppingList || typeof parsedShoppingList !== 'object')
return;
setShoppingList(parsedShoppingList);
})
.then((err) => {
console.warn(err);
});
};
const RenderItem = ({ item }) => {
return (
<View>
<TouchableOpacity
style={{
marginLeft: 20,
marginRight: 20,
elevation: 3,
backgroundColor: colors.card,
borderRadius: 10,
}}>
<View style={{ margin: 10 }}>
<Text style={{ color: colors.text, fontWeight: '700' }}>
{item.name}
</Text>
<Text style={{ color: colors.text }}>{item.gluten}</Text>
<Text style={{ color: colors.text }}>{item.id}</Text>
</View>
</TouchableOpacity>
</View>
);
};
const emptyComponent = () => {
return (
<View style={{ alignItems: 'center' }}>
<Text style={{ color: colors.text }}>Listan är tom</Text>
</View>
);
};
const itemSeparatorComponent = () => {
return (
<View
style={{
margin: 3,
}}></View>
);
};
return (
<View
style={{
flex: 1,
}}>
<View
style={{
padding: 30,
backgroundColor: colors.Textinput,
elevation: 12,
}}>
<View style={{ flexDirection: 'row', justifyContent: 'space-between' }}>
<TouchableOpacity onPress={() => navigation.goBack()}>
<Ionicons name="arrow-back-outline" size={25} color="#fff" />
</TouchableOpacity>
<Text style={{ color: '#fff', fontSize: 20 }}>Inköpslista</Text>
<TouchableOpacity>
<Ionicons
name="trash-outline"
size={25}
color="#fff"
onPress={() => todo()}
/>
</TouchableOpacity>
</View>
</View>
<View style={{ flex: 1, marginTop: 30 }}>
<FlatList
data={shoppingList}
renderItem={RenderItem}
ListEmptyComponent={emptyComponent}
ItemSeparatorComponent={itemSeparatorComponent}
initialNumToRender={4}
maxToRenderPerBatch={5}
windowSize={10}
removeClippedSubviews={true}
updateCellsBatchingPeriod={100}
showsVerticalScrollIndicator={true}
contentContainerStyle={{ paddingBottom: 20 }}
/>
</View>
</View>
);
}
As you are using async storage to maintain the cart.
I would suggest an approach as below
Update the asyn storage when new items are added to or removed from the cart
Retrieve the items from the cart screen and show the items there
Before you navigate store the items like below
AsyncStorage.setItem(
'Items',
JSON.stringify(updated.filter((item) => item.selected))
).then(() => {
navigation.navigate('Cart', {
items: updated.filter((item) => item.selected),
});
});
The cart screen would be something like below
function Cart({ navigation, route }) {
const [data,setData]=useState([]);
React.useEffect(() => {
async function fetchMyAPI() {
const value = await AsyncStorage.getItem('Items');
setData(JSON.parse(value));
}
fetchMyAPI();
}, []);
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Button title="Go back" onPress={() => navigation.goBack()} />
<FlatList
data={data}
renderItem={RenderItem}
/>
</View>
);
}
Working Example
https://snack.expo.io/#guruparan/cartexample
I'm trying to get each button to activate and "switch-on" when pressed, and I've used some documentation to help me. However, now it is not switching on.
Code Adds Switches in a FlatList
The Data should look like this:
https://imgur.com/a/761PSjre
Also, feel free to judge my code to the darkest depths of hell. I'm trying to improve my code.
import React from 'react'
import {StyleSheet, View,Text, Switch, Button, Alert, ScrollView, FlatList, SafeAreaView} from 'react-native'
export default () => {
const DATA = [
{
index: 1,
title: 'Toggle Night Mode',
},
{
index: 2,
title: 'Remind me to take a break',
},
{
index: 3,
title: "Remind me when it's bedtime",
},
];
const [enabledSwitches, setEnabledSwitches] = React.useState(DATA.length);
const toggleSwitch = () => setEnabledSwitches(previousState => !previousState);
function Item({title, index}) {
return (
<View>
<Text style={styles.text}> {title} </Text>
<Switch
trackColor={{ false: "#767577", true: "#81b0ff" }}
thumbColor="#f5dd4b"
ios_backgroundColor="#3e3e3e"
value={enabledSwitches[index]}
onValueChange={() => toggleSwitch(switches => {
switches[index] = !switches[index];
return switches;
})}
/>
</View>
)
}
function Header(){
return(
<View style = {styles.header}>
<Text style={styles.headertext}>Settings</Text>
</View>
)
}
return (
<>
<View style = {styles.container}>
<FlatList
data = {DATA}
keyExtractor = {item => item.id}
renderItem = {({ item, index }) => <Item title={item.title} index={index} /> }
ListHeaderComponent = {Header()}
/>
</View>
<View>
<Button
title = "Clear Search History"
color = "#6fb6f0"
onPress = {() => Alert.alert('Food History Has Been Cleared!')}
/>
</View>
<View>
<Button
title = "Logout"
color = "#6fb6f0"
onPress = {() => Alert.alert('Successfully Logged Out!')}
/>
</View>
</>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
text: {
fontSize: 20,
fontWeight: "300"
},
headertext: {
fontSize: 30,
fontWeight: "300"
},
header:{
flex:1,
justifyContent: 'center',
alignItems: 'center',
padding: 10,
backgroundColor: '#f5f5f5'
}
})
Try this way
import React from 'react'
import {StyleSheet, View,Text, Switch, Button, Alert, ScrollView, FlatList, SafeAreaView} from 'react-native'
export default () => {
// use data set in default state
const [data, setData] = React.useState([ {index: 1,title: 'Toggle Night Mode'},...]);
function toggleSwitch(value, index){
const newData = [...data];
const newData[index].isEnable = value;
setData(newData);
}
function Item({item, index}) {
return (
<View>
<Text style={styles.text}> {item.title} </Text> // use `title` here like this
<Switch
.....
value={item.isEnable || false} // change here
onValueChange={(value) => toggleSwitch(value, index) } // change here
/>
</View>
)
}
return (
<>
<View style = {styles.container}>
<FlatList
data = {data}
keyExtractor = {item => item.id}
renderItem = {({ item, index }) => <Item item={item} index={index} /> } // send `item` as prop
/>
</View>
</>
);
}
in my app on my homescreen I am rendering a ListList with my items. Now I wanted to add a little info text above it. But it only shows my text and skips(?) my FlatList. Could anyone help me and explain me this behaviour?
If I have my text component in my file it only shows the text. If I only use FlatList it shows correctly my list with my data. But if I try to combine both, it only shows one component. Same thing when I use only FlatList and wrap it inside of View then I get only a white blank screen.
const JobsScreen = (props) => {
const dispatch = useDispatch();
const [isLoading, setIsLoading] = useState(false);
const [isRefreshing, setIsRefreshing] = useState(false);
const allJobs = useSelector((state) => state.job.availableJobs);
const loadJobs = useCallback(async () => {
setIsRefreshing(true);
try {
await dispatch(jobActions.fetchAllJobs());
} catch (err) {}
setIsRefreshing(false);
}, [dispatch]);
useEffect(() => {
setIsLoading(true);
loadJobs().then(() => {
setIsLoading(false);
});
}, [dispatch, loadJobs]);
useEffect(() => {
const willFocusSub = props.navigation.addListener("willFocus", loadJobs);
return () => {
willFocusSub.remove();
};
}, [dispatch, loadJobs]);
if (isLoading) {
return (
<View
style={{
flex: 1,
justifyContent: "center",
alignItems: "center",
backgroundColor: "#2f3640",
}}
>
<ActivityIndicator size="large" color="#fff" animating />
</View>
);
}
return (
<View>
<FlatList
data={allJobs}
onRefresh={loadJobs}
refreshing={isRefreshing}
keyExtractor={(item) => item.id}
style={{ flex: 1, backgroundColor: "#1a191e" }}
renderItem={(itemData) => (
<JobItem
description={itemData.item.description}
titel={itemData.item.titel}
fname={itemData.item.fname}
cover={itemData.item.cover}
genre={itemData.item.genre}
year={itemData.item.year}
id={itemData.item.id}
// friend={itemData.item.friend}
/>
)}
/>
</View>
);
};
Got it by myself.
<View style={{ height: "100%" }}>
solved it.
Try this.
<View style={{ flex: 1 }}>