Card drop-down on react native - react-native

Is it possible to achieve a component drop on react native?
Something like this. Sorry for the paint skills. Thank you in advance .

You can create a custom component like below, You can change the render label to support the view you want.
This sample needs the data to be like the below object but you can create your own object.
const dataArray = [{value: 'Car', id: 1,},{value: 'Van',id: 2},{ value: 'Bus',id: 3}];
//Usage
<CustomDropDown data={dataArray} />
const CustomDropDown = (props) => {
const [open, setOpen] = React.useState(false);
const [selected, setSelected] = React.useState(-1);
const renderLabel = (item) => {
return (
<View
style={{
width: 200,
height: 60,
borderWidth: 1,
borderRadius: 10,
padding: 5,
marginVertical: 3,
}}>
<Text style={{ fontSize: 20 }}>{item.value}</Text>
<View style={{ flexDirection: 'row', justifyContent: 'space-between' }}>
<Text>{item.value}</Text>
<Text style={{ fontSize: 15 }}>{item.value}</Text>
</View>
</View>
);
};
const renderItem = (item) => {
return (
<TouchableOpacity
onPress={() => {
setSelected(item.id);
setOpen(!open);
//Call back to external state
}}>
{renderLabel(item)}
</TouchableOpacity>
);
};
return (
<View>
<TouchableOpacity style={{}} onPress={() => setOpen(!open)}>
{selected === -1 && <Text>Place holder text</Text>}
{selected !== -1 &&
renderLabel(props.data.find((x) => x.id === selected))}
</TouchableOpacity>
{open && (
<View
style={{
position: 'absolute',
top: 70,
}}>
{props.data.map((item) => renderItem(item))}
</View>
)}
</View>
);
};

Related

Force BottomSheetFlatList open from first position every time useCallback called in react-native-bottom-sheet

I am using react-native-bottom-sheet with BottomSheetFlatList.
I want to open BottomSheetFlatList at initialPosition every time it opened but I am not able to achieve that. When I open it it's always open at last scrolling position.
<BottomSheet
ref={bottomSheetRef}
index={0}
snapPoints={snapPoints}
enableDismissOnClose={true}
handleIndicatorStyle={{display: 'none'}}
handleStyle={{padding: 0}}>
<View style={{backgroundColor: '#b2b2b2', alignItems: 'center'}}>
{!isOpen && (
<Pressable onPress={() => handleSnapPress(1)}>
<Image
source={IconCollapse}
style={{width: 30, height: 20, backgroundColor: '#b2b2b2'}}
/>
</Pressable>
)}
{isOpen && (
<Pressable onPress={() => handleClosePress()}>
<Image
source={IconExpand}
style={{width: 30, height: 20, backgroundColor: '#b2b2b2'}}
/>
</Pressable>
)}
</View>
<View style={{backgroundColor: '#b2b2b2'}}>
<Text
style={{
color: 'white',
fontSize: 20,
textTransform: 'uppercase',
paddingStart: 10,
}}>
Select Zone
</Text>
<View
style={{
borderBottomColor: '#fff',
borderBottomWidth: 1,
marginEnd: 10,
marginStart: 10,
}}
/>
</View>
<BottomSheetFlatList
data={zoneList}
keyExtractor={(item, index) => `${index}`}
renderItem={renderItem}
contentContainerStyle={styles.contentContainer}
/>
</BottomSheet>
you can try
this.flatListRef.scrollToOffset({ animated: true, offset: 0 });
hope this is work
use Prop enableDismissOnClose
as mentioned here
As Mohammad Momin said you can try scrollToIndex({index: 0, offset: 0}).
In order to do that you have to declare a ref for your FlatList and also specify the getItemLayout prop. Then you can call scrollToIndex.
So every time that you open the bottomSheet you have to call scrollToIndex.
The full working example I created is available in this github repositor which is something like this:
export const CustomBottomSheet = forwardRef(({
zoneList = [],
selectedZone,
onZoneSelected
}, ref) => {
const bottomSheetRef = useRef(null);
const flatListRef = useRef(null);
const scrollToItem = () => {
if (!selectedZone) return;
if (zoneList.length < 1) return;
// find item index
const index = zoneList.findIndex(value => value.id === selectedZone?.id);
// scroll to destination index
// it's better to set animated to true (experimental)
flatListRef.current.scrollToIndex({
animated: true,
index: index, // 0
})
console.log('scrollToItem called by index:' + index)
}
const handleSnapPress = (index = 0) => {
bottomSheetRef.current.snapToIndex(index);
// call this method after each time user opens the bottom sheet
scrollToItem();
}
const handleClosePress = () => {
bottomSheetRef.current.snapToIndex(index);
}
const renderItem = ({ item }) => {
return (
<ListItem
{...item}
onPress={() => onZoneSelected(item)}
isSelected={item.id === selectedZone?.id}
/>
)
}
const getItemLayout = (_, index) => (
{
length: ListItem.HEIGHT,
offset: (ListItem.HEIGHT + ListItem.VERTICAL_SPACING) * index,
index: index,
}
)
// forwarding methods via ref
useImperativeHandle(ref, () => ({
open: handleSnapPress,
close: handleClosePress,
snapToIndex: handleSnapPress,
}));
return (
<BottomSheet
ref={bottomSheetRef}
index={-1}
snapPoints={SNAP_POINTS}
enableDismissOnClose={true}
// onChange={handleOnChange}
enablePanDownToClose={true}
>
<View style={{ backgroundColor: '#b2b2b2', alignItems: 'center', marginTop: 16 }}>
<Text
style={{
color: 'white',
fontSize: 20,
textTransform: 'uppercase',
paddingStart: 10,
}}>
{'Select Zone'}
</Text>
</View>
<BottomSheetFlatList
// add ref and getItemLayout in order to use scrollToIndex method
ref={flatListRef}
getItemLayout={getItemLayout}
data={zoneList}
keyExtractor={(item) => item.id}
renderItem={renderItem}
contentContainerStyle={styles.contentContainer}
/>
</BottomSheet>
)
})

flatlist slow performance on large data react native

I'm new to react native and I'm having some performance problem with flatlist in React Native Expo. On my screen I'm displaying a list of contacts and I want them to be selected or deselected and I have managed to achieve that part but the problem is that it takes a lot of visibility time to check the contacts or to uncheck them. Making the screen not very user friendly. I have tried a lot of solutions that I've found on internet regarding this problem but none of them had worked. Hope someone can help me. Below I'll attach the code!
function ContactList() {
const [itemChecked, setItemChecked] = useState([]);
const [checked, setChecked] = useState(false);
const [contacts, setContacts] = useState([]);
const [filter, setFilter] = useState([]);
const [search, setSearch] = useState('');
const [checkedBox, setCheckedBox] = useState(false);
useEffect(() => {
(async () => {
const { status } = await Contacts.requestPermissionsAsync();
if (status === 'granted') {
const { data } = await Contacts.getContactsAsync({
fields: [Contacts.Fields.PhoneNumbers],
// fields: [Contacts.Fields.Name],
});
if (data.length > 0) {
setContacts(data);
setFilter(data);
// console.log('contact', contacts[1]);
// console.log('filter', filter);
}
}
})();
}, []);
const searchFilter = (text) => {
if (text) {
const newData = contacts.filter((item) => {
const itemData = item.name ? item.name.toUpperCase() : ''.toUpperCase();
const textData = text.toUpperCase();
return itemData.indexOf(textData) > -1;
});
setFilter(newData);
setSearch(text);
} else {
setFilter(contacts);
setSearch(text);
}
};
const onChangeValue = (item, index) => {
if (itemChecked.includes(item.phoneNumbers[0].digits)) {
itemChecked.splice(itemChecked.indexOf(item.phoneNumbers[0].digits), 1);
} else {
itemChecked.push(item.phoneNumbers[0].digits);
setCheckedBox(true);
}
setItemChecked(itemChecked);
console.log(itemChecked);
// console.log(item);
};
const renderItem = ({ item, index }) => {
return (
<SafeAreaView>
<ScrollView>
<TouchableOpacity style={{ flexDirection: 'row', flex: 1 }}>
<View style={{ flex: 1, borderTopWidth: 0.5, borderTopColor: 'grey', marginBottom: 15 }}>
<Text onPress={() => setChecked(true)} style={{ fontSize: 20, marginHorizontal: 10 }}>
{item.name + ' '}
</Text>
<Text style={{ fontSize: 17, marginHorizontal: 10, marginTop: 5, color: 'grey' }}>
{item.phoneNumbers && item.phoneNumbers[0] && item.phoneNumbers[0].number}
</Text>
</View>
<View style={{ flex: 1, borderTopWidth: 0.5, borderTopColor: 'grey' }}>
{itemChecked.includes(item.phoneNumbers[0].digits) === false ? (
<CheckBox
style={{ width: 15, height: 15 }}
right={true}
checked={false}
onPress={() => {
onChangeValue(item, index);
}}
/>
) : (
<CheckBox
style={{ width: 15, height: 15, paddingTop: 8 }}
right={true}
checked={true}
onPress={() => {
onChangeValue(item, index);
}}
/>
)}
</View>
{/* <Post postJson={item} isGroupAdmin={isGroupAdmin} user={user} /> */}
</TouchableOpacity>
</ScrollView>
</SafeAreaView>
);
};
return (
<SafeAreaView style={styles.container}>
<View style={styles.container}>
<View
style={{
height: 40,
justifyContent: 'center',
backgroundColor: '#eeeeee',
width: '90%',
marginHorizontal: 20,
marginTop: 15,
borderRadius: 10,
}}
>
<Feather name="search" size={20} color="grey" style={{ position: 'absolute', left: 32 }} />
<TextInput
placeholder="Search"
placeholderTextColor="#949494"
style={{
left: 20,
paddingHorizontal: 35,
fontSize: 20,
}}
value={search}
onChangeText={(text) => {
searchFilter(text);
setSearch(text);
}}
/>
</View>
<FlatList
style={{ marginTop: 15 }}
data={contacts && filter}
keyExtractor={(item) => `key-${item.id.toString()}`}
renderItem={renderItem}
refreshing={true}
initialNumToRender={10}
ListEmptyComponent={<Text message="No contacts found." />}
/>
</View>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
},
});
export default ContactList;

react native modal takes 2 second to open

In my app you can join a group. There are storys and messages. If there is no story or messages modal is open fast. But If I have 30 text messages to render and want to click to open my modal then it takes 1-2 seconds or later to open. The same issue if I click a button. The opacity and function works until 1-2 seconds but if there is no much messages to render then it goes fast. I need help.
message render function
<FlatList
data={globalMessages}
style={{width: '100%'}}
contentContainerStyle={{marginLeft: 12}}
renderItem={({ item }) => (
<TouchableOpacity onLongPress={() => deleteMessage(item.id)} style={{width: 230, flexDirection: 'column', backgroundColor: '#eee', padding: 8, borderRadius: 12, margin: 12, marginLeft: 0, marginTop: 0}}>
<Text style={{fontFamily: 'poppins-light', fontSize: 16, color: '#333'}}>{item.message}</Text>
<Text style={{fontFamily: 'poppins-medium', fontSize: 12, color: '#333'}}>{item.date}</Text>
</TouchableOpacity>
)}
keyExtractor={item => item.id.toString()}
// ListHeaderComponent={() => this.renderHeader()}
horizontal={false}
nestedScrollEnabled
showsHorizontalScrollIndicator={false}
// Performance settings
removeClippedSubviews={true} // Unmount components when outside of window
initialNumToRender={2} // Reduce initial render amount
maxToRenderPerBatch={1} // Reduce number in each render batch
updateCellsBatchingPeriod={100} // Increase time between renders
windowSize={4} // Reduce the window size
/>
fullCode:
import { StatusBar } from 'expo-status-bar';
import React, { useState, useEffect, useRef } from 'react';
import { StyleSheet, Text, TextInput, View, TouchableOpacity, Image, Dimensions, ImageBackground, ActivityIndicator, ScrollView, FlatList, TouchableWithoutFeedback } from 'react-native';
import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view';
import { AntDesign, Ionicons } from '#expo/vector-icons';
import Modal from 'react-native-modal';
import * as ImagePicker from 'expo-image-picker';
import uploadStoryAPI from '../api/uploadStory';
import createMessageAPI from '../api/createMessage';
import io from 'socket.io-client/dist/socket.io';
const width = Dimensions.get('window').width;
const height = Dimensions.get('window').height;
const Room = ({ route }) => {
const socket = io('http://192.168.0.249:3000', {
transports: ['websocket']
});
const [connected, setConnected] = useState(false);
const [areaHeight, setAreaHeight] = useState(0);
const [chatMessage, setChatMessage] = useState('');
const [globalMessages, setGlobalMessages] = useState(route.params.messages || []);
const [loadSendChatMessage, setLoadSendChatMessage] = useState(false);
const [alignItem, setAlignItem] = useState('center');
const [addStory, setAddStory] = useState(false);
const [storys, setStorys] = useState(route.params.storys || []);
const [modalImagePreview, setModalImagePreview] = useState(false);
const [modalDeleteMessage, setModalDeleteMessage] = useState(false);
const [imagePreview, setImagePreview] = useState('');
const [loadUpload, setLoadUpload] = useState(false);
const [sourceStory, setSourceStory] = useState({
uri: null,
type: null,
name: null
});
useEffect(() => {
socket.on('connect', () => {
setConnected(true);
console.log('user connected');
});
socket.on('getmessage', message => {
globalMessages.length > 0 ?
setGlobalMessages(prevState => {
return [...prevState, message]
}) : setGlobalMessages([message]);
handleAddMessage();
});
});
const handleAlign = () => {
areaHeight > 100 ? setAlignItem('flex-end') : setAlignItem('center');
}
const handleAddStory = async () => {
try {
let result = await ImagePicker.launchImageLibraryAsync({
allowsEditing: true,
aspect: [1, 1]
});
setAddStory(false);
if(result.cancelled) {
setAddStory(false);
return;
}
let localUri = result.uri;
let filename = localUri.split('/').pop();
// Infer the type of the image
let match = /\.(\w+)$/.exec(filename);
let type = match ? `image/${match[1]}` : `image`;
setImagePreview(localUri);
setSourceStory({
uri: localUri,
type,
filename
});
setModalImagePreview(true);
} catch(e) {
return;
}
};
const handleUploadToServer = async () => {
try {
setLoadUpload(true);
let res = await uploadStoryAPI(route.params.roomID, sourceStory);
res.upload && storys.length > 0
? setStorys(prevState => {
return [{
source: res.source
}, ...prevState]
}) : setStorys([{ source: res.source}]);
console.log(`D ${storys}`)
res.upload ? (setLoadUpload(false),setModalImagePreview(false)) : setLoadUpload(false);
} catch(e) {
return e;
}
};
const handleAddMessage = async () => {
try {
setLoadSendChatMessage(true);
const res = await createMessageAPI(route.params.roomID, chatMessage);
console.log(res);
//scrollViewRef.current.scrollToEnd({animated: true});
setLoadSendChatMessage(false);
} catch(e) {
console.log(e);
return e;
}
};
const deleteMessage = async id => {
try {
console.log(id);
} catch(e) {
return e;
}
};
return (
<KeyboardAwareScrollView keyboardShouldPersistTaps="handled" style={{margin: 0, padding: 0}}>
<View style={{height: height - 155, backgroundColor: '#fff'}}>
<View>
<ScrollView>
<Text style={styles.title}>Storys</Text>
{
storys.length > 0
?
<FlatList
data={storys}
contentContainerStyle={{marginLeft: 8}}
renderItem={({ item }) => (
<TouchableOpacity key={`index-${item.source}`} style={{padding: 0, marginRight: 16, borderWidth: 2, justifyContent: 'center', alignItems: 'center', borderRadius: 100, borderColor: '#1ab7ff', height: 68, width: 68}}>
<Image resizeMode="contain" source={{uri: `http://192.168.0.249:3000/build/${item.source}`}} style={{height: 55, width: 55, borderRadius: 500}} />
</TouchableOpacity>
)}
keyExtractor={item => item.source.toString()}
// ListHeaderComponent={() => this.renderHeader()}
nestedScrollEnabled
horizontal
showsHorizontalScrollIndicator={false}
// Performance settings
removeClippedSubviews={true} // Unmount components when outside of window
initialNumToRender={2} // Reduce initial render amount
maxToRenderPerBatch={1} // Reduce number in each render batch
updateCellsBatchingPeriod={100} // Increase time between renders
windowSize={7} // Reduce the window size
/>
:
<Text>Es sind zurzeit keine Storys vorhanden.</Text>
}
<Text style={styles.title}>Nachrichten</Text>
{
globalMessages.length > 0
?
<FlatList
data={globalMessages}
style={{width: '100%'}}
contentContainerStyle={{marginLeft: 12}}
renderItem={({ item }) => (
<TouchableOpacity onLongPress={() => deleteMessage(item.id)} style={{width: 230, flexDirection: 'column', backgroundColor: '#eee', padding: 8, borderRadius: 12, margin: 12, marginLeft: 0, marginTop: 0}}>
<Text style={{fontFamily: 'poppins-light', fontSize: 16, color: '#333'}}>{item.message}</Text>
<Text style={{fontFamily: 'poppins-medium', fontSize: 12, color: '#333'}}>{item.date}</Text>
</TouchableOpacity>
)}
keyExtractor={item => item.id.toString()}
// ListHeaderComponent={() => this.renderHeader()}
horizontal={false}
nestedScrollEnabled
showsHorizontalScrollIndicator={false}
// Performance settings
removeClippedSubviews={true} // Unmount components when outside of window
initialNumToRender={2} // Reduce initial render amount
maxToRenderPerBatch={1} // Reduce number in each render batch
updateCellsBatchingPeriod={100} // Increase time between renders
windowSize={4} // Reduce the window size
/>
: null
}
</ScrollView>
</View>
</View>
<View style={{flex: 1, paddingLeft: 16, paddingTop: 8, paddingRight: 16, paddingBottom: 12, backgroundColor: '#fff', alignItems: alignItem, justifyContent:'space-between', flexDirection: 'row'}}>
<TouchableOpacity onPress={() => setAddStory(true)}>
<AntDesign name="pluscircleo" size={32} color="#444" />
</TouchableOpacity>
<TextInput
onChangeText={e => setChatMessage(e)}
// onFocus={() => setAlignItem('flex-end')}
onEndEditing={() => handleAlign()}
value={chatMessage}
multiline={true}
style={[styles.input, {height: Math.max(50, areaHeight)}]}
onContentSizeChange={e => {
setAreaHeight(e.nativeEvent.contentSize.height);
}}
placeholder="Gebe eine Nachricht ein..." />
<TouchableOpacity onPress={() => chatMessage.length > 0 ? socket.emit('getmessage', {
chatMessage,
date: Date.now()
}) : null} style={{padding: 8, paddingRight: 10, borderRadius: 8, backgroundColor: '#1ab7ff'}}>
{ !loadSendChatMessage ? <Ionicons name="paper-plane-outline" size={28} color="#fff" /> : <ActivityIndicator style={{height: 20, width: 20}} color="#fff" />}
</TouchableOpacity>
</View>
{ /* Ask user if he wants to upload */}
<Modal
isVisible={modalImagePreview}
swipeDirection="down"
onSwipeComplete={() => setModalImagePreview(false)}
onBackButtonPress={() => setModalImagePreview(false)}
onBackdropPress={() => setModalImagePreview(false)}
backdropTransitionOutTiming={0}
// style={{margin: 0, padding: 0}}
>
<View style={{ backgroundColor: '#fff', borderRadius: 12, padding: 12}}>
<Text style={{fontFamily: 'poppins-light', color: '#333', fontSize: 18, textAlign: 'center', marginBottom: 10}}>Möchtest du diese Story hochladen?</Text>
{ <Image resizeMode="contain" style={{height: 250, borderRadius: 12}} source={{uri: imagePreview}} /> }
<View style={{flexDirection: 'row', justifyContent: 'space-around', alignItems: 'center', marginTop: 20, marginBottom: 10}}>
<TouchableOpacity onPress={() => setModalImagePreview(false)}>
<Text style={{fontFamily: 'poppins-light', color: '#333', borderWidth: 1, borderColor: '#eee', padding: 10, paddingTop: 13, borderRadius: 8}}>Abbrechen</Text>
</TouchableOpacity>
<TouchableOpacity onPress={() => handleUploadToServer()} style={{backgroundColor: '#1ab7ff', padding: 10, paddingTop: 13, borderRadius: 8}}>
{ !loadUpload ? <Text style={{fontFamily: 'poppins-light', color: '#fff'}}>Story hinzufügen</Text> : <ActivityIndicator style={{height: 20, width: 20}} color="#fff" /> }
</TouchableOpacity>
</View>
</View>
</Modal>
{ /* Modal open Story */ }
<Modal
isVisible={addStory}
swipeDirection="down"
onSwipeComplete={() => setAddStory(false)}
onBackButtonPress={() => setAddStory(false)}
onBackdropPress={() => setAddStory(false)}
backdropTransitionOutTiming={0}
style={{margin: 0, padding: 0}}
>
<View style={{position: 'absolute', bottom: 0, backgroundColor: '#fff', width: '100%', justifyContent: 'center', alignItems: 'center', borderTopLeftRadius: 20, borderTopRightRadius: 20}}>
<TouchableOpacity onPress={() => handleAddStory()} style={{padding: 20, backgroundColor: '#1ab7ff', width: width, justifyContent: 'center', alignItems: 'center',borderTopLeftRadius: 20, borderTopRightRadius: 20}}>
<Text style={{color: '#fff', fontFamily: 'poppins-light'}}>Story hinzufügen</Text>
</TouchableOpacity>
<TouchableOpacity onPress={() => setAddStory(false)} style={{padding: 20}}>
<Text>Abbrechen</Text>
</TouchableOpacity>
</View>
</Modal>
</KeyboardAwareScrollView>
)
};

Webview Uri Redirect by scanning barcodes with React Native, Expo

I am creating a barcode scanner using React Native and Expo.
I am trying to pass a new URL to a WebView after scanning a barcode.
But I am not sure how.
Please help.
export default function App() {
const [hasPermission, setHasPermission] = useState(null);
const [scanned, setScanned] = useState(false);
const [modalVisible, setModalVisible] = useState(false);
useEffect(() => {
(async () => {
const { status } = await BarCodeScanner.requestPermissionsAsync();
setHasPermission(status === 'granted');
})();
}, []);
const handleBarCodeScanned = ({ type, data }) => {
setScanned(true);
setModalVisible(true);
};
if (hasPermission === null) {
return <Text>Requesting for camera permission</Text>;
}
if (hasPermission === false) {
return <Text>No access to camera</Text>;
}
return (
<View
style={{
flex: 1,
flexDirection: 'column'
}}>
<Modal
animationType="slide"
transparent={false}
visible={modalVisible}
onRequestClose={() => {
setScanned(false);
}}>
<View style={{ flex: 1 }}>
<WebView
style={{ flex: 1 }}
source={{ uri: 'http://domain.biz/' }}
/>
<TouchableHighlight
style={{
backgroundColor:'black',
padding: 15,
alignItems: 'center'
}}
onPress={() => {
setModalVisible(!modalVisible);
setScanned(false);
}}
underlayColor='slategray'
>
<Text style={{ color:'white', fontSize: 15 }}>Re Scan</Text>
</TouchableHighlight>
</View>
</Modal>
<BarCodeScanner
onBarCodeScanned={scanned ? undefined : handleBarCodeScanned}
style={{
flex: 1,
justifyContent: 'center',
alignItems: 'center',
}}>
<View style={{ marginBottom: 100 }}>
<View style={{ alignItems: 'center', marginBottom: 5 }}>
<Image
style={{
width: 100,
height: 100,
resizeMode: 'contain',
marginBottom: 20,
}}
source={{ uri: 'http://domain.biz/img/logo_dark.png' }}
/>
<Text style={{ color: 'white', fontSize: 20, fontWeight: 'bold', paddingBottom: 10}}>
QR Code Reader v0.5
</Text>
</View>
<View
style={{
borderColor: 'white',
borderTopWidth: 5,
borderBottomWidth: 5,
borderLeftWidth: 1,
borderRightWidth: 1,
paddingVertical: 80,
paddingHorizontal: 100,
}}
/>
<View style={{ alignItems: 'center', marginTop: 5 }}>
<Text style={{ color: 'white', fontSize: 15}}>
QR Scan...
</Text>
</View>
</View>
</BarCodeScanner>
</View>
);
}
I am creating a barcode scanner using React Native and Expo.
I am trying to pass a new URL to a WebView after scanning a barcode.
But I am not sure how.
Please help.
This should do the work. I save the url from the scanner in state.uri and use that instead of a static string.
For testing purposes I used this barcode which leads to this answer:
Best, Paul
import React, { useState, useEffect } from 'react';
import { Text, View, StyleSheet, Button, Modal, TouchableHighlight, Image } from 'react-native';
import { WebView } from 'react-native-webview';
import { BarCodeScanner } from 'expo-barcode-scanner';
export default function App(){
const [hasPermission, setHasPermission] = useState(null);
const [scanned, setScanned] = useState(true);
const [modalVisible, setModalVisible] = useState(true);
const [uri, setUri] = useState('https://stackoverflow.com/questions/61977154/webview-uri-redirect-by-scanning-barcodes-with-react-native-expo');
useEffect(() => {
(async () => {
const { status } = await BarCodeScanner.requestPermissionsAsync();
setHasPermission(status === 'granted');
})();
}, []);
const handleBarCodeScanned = ({ type, data }) => {
setScanned(true);
setModalVisible(true);
// console.warn("Scan returned " + data);
setUri({ uri: data })
};
if (hasPermission === null) {
return <Text>Requesting for camera permission</Text>;
}
if (hasPermission === false) {
return <Text>No access to camera</Text>;
}
return (
<View
style={{
flex: 1,
flexDirection: 'column'
}}>
<Modal
animationType="slide"
transparent={false}
visible={modalVisible}
onRequestClose={() => {
setScanned(false);
}}>
<View style={{ flex: 1 }}>
<WebView
style={{ flex: 1 }}
source={{uri: uri['uri']}}
/>
<TouchableHighlight
style={{
backgroundColor:'black',
padding: 15,
alignItems: 'center'
}}
onPress={() => {
setModalVisible(!modalVisible);
setScanned(false);
}}
underlayColor='slategray'
>
<Text style={{ color:'white', fontSize: 15 }}>Re Scan</Text>
</TouchableHighlight>
</View>
</Modal>
<BarCodeScanner
onBarCodeScanned={scanned ? undefined : handleBarCodeScanned}
style={{
flex: 1,
justifyContent: 'center',
alignItems: 'center',
}}>
<View style={{ marginBottom: 100 }}>
<View style={{ alignItems: 'center', marginBottom: 5 }}>
<Image
style={{
width: 100,
height: 100,
resizeMode: 'contain',
marginBottom: 20,
}}
source={{ uri: 'http://domain.biz/img/logo_dark.png' }}
/>
<Text style={{ color: 'white', fontSize: 20, fontWeight: 'bold', paddingBottom: 10}}>
QR Code Reader v0.5
</Text>
</View>
<View
style={{
borderColor: 'white',
borderTopWidth: 5,
borderBottomWidth: 5,
borderLeftWidth: 1,
borderRightWidth: 1,
paddingVertical: 80,
paddingHorizontal: 100,
}}
/>
<View style={{ alignItems: 'center', marginTop: 5 }}>
<Text style={{ color: 'white', fontSize: 15}}>
QR Scan...
</Text>
</View>
</View>
</BarCodeScanner>
</View>
);
}
Here it is, but I have passed the data from the other page name:index.js.
You can use the linking component to open every URL passed through {data}
import React from "react";
import { View, Text, Linking, TouchableOpacity } from "react-native";
const detail = ({ route, navigation }) => {
const data = route.params;
const [uri, setUri] = React.useState({});
return (
<View>
<TouchableOpacity onPress={() => Linking.openURL(data)}>
<Text>{data}</Text>
</TouchableOpacity>
</View>
);
};
export default detail;

can not use delete functionality in flatlist renderItem

Delete functionality is not working in flatlist renderItem method, but it will work perfectly fine if I use map function to render data instead of flatlsit.
Here is the sample code
class App extends Component {
state = {
todos: [
{ todo: 'go to gym', id: 1 },
{ todo: 'buy a mouse', id: 2 },
{ todo: 'practice hash table', id: 3 },
{ todo: 'iron clothes', id: 4 }
]
};
keyExtractor = item => item.id.toString();
handleDelete = id => {
const todos = this.state.todos.filter(item => item.id !== id);
this.setState({ todos });
};
renderItems({ item }) {
return (
<View
style={{
display: 'flex',
flexDirection: 'row',
justifyContent: 'space-between'
}}
>
<Text style={{ fontSize: 16 }}>{item.todo}</Text>
<TouchableOpacity
onPress={() => this.handleDelete(item.id)}
style={{ marginRight: 15 }}
>
<Text style={{ color: 'red' }}>Delete</Text>
</TouchableOpacity>
</View>
);
}
render() {
return (
<View>
{/* {this.renderItems()} */}
<FlatList
data={this.state.todos}
keyExtractor={this.keyExtractor}
renderItem={this.renderItems}
/>
</View>
);
}
}
I can't understand the reason it gives me the error _this2.handleDelete is not a function.
You were not binding your function, in your constructor bind the function or use array function
renderItems = ({ item }) => {
return (
<View
style={{
display: 'flex',
flexDirection: 'row',
justifyContent: 'space-between',
}}>
<Text style={{ fontSize: 16 }}>{item.todo}</Text>
<TouchableOpacity
onPress={() => this.handleDelete(item.id)}
style={{ marginRight: 15 }}>
<Text style={{ color: 'red' }}>Delete</Text>
</TouchableOpacity>
</View>
);
}