Expo React Native: BarcodeScanner.requestPermissionsAsync() doesnt apply a permission request - react-native

I'm trying to build a Barcode scanner component and I'm using expo-barcode-scanner.
the console.log works fine and I get the ASKING text.
the dialog window doesnt show up
here is my code:
import React, { useState, useEffect } from 'react';
import { Text,SafeAreaView, View, StyleSheet, Button } from 'react-native';
import { BarCodeScanner } from 'expo-barcode-scanner';
const ScannerComponent =()=> {
const [hasPermission, setHasPermission] = useState(null);
const [scanned, setScanned] = useState(false);
const askForCameraPermission = () =>{
console.log('ASKING')
async () => {
const {status} = await BarCodeScanner.requestPermissionsAsync();
setHasPermission(status === 'granted');
}
}
useEffect(() => {
askForCameraPermission();
}, []);
const handleBarCodeScanned = ({ type, data }) => {
setScanned(true);
alert(`Bar code with type ${type} and data ${data} has been scanned!`);
};
if (hasPermission === null) {
return (<View style={styles.container}><Text style={styles.text}>Requesting for camera permission</Text></View>);
}
if (hasPermission === false) {
return (<View style={styles.container}><Text style={styles.text}>No access to camera</Text></View>);
}
return (
<SafeAreaView style={styles.container}>
<Text value='hello' />
<View style={styles.scanner}>
<BarCodeScanner
onBarCodeScanned={scanned ? undefined : handleBarCodeScanned}
style={StyleSheet.absoluteFillObject}
/>
</View>
{scanned && <Button title={'Tap to Scan Again'} onPress={() => setScanned(false)} />}
</SafeAreaView>
);
}
export default ScannerComponent

Related

React native expo camera "no access to camera" error

I have installed the expo camera tool with npm.
I run this code and it works on Snack :
import React, { useState, useEffect } from "react";
import { Text, View, StyleSheet, Button } from "react-native";
import { BarCodeScanner } from "expo-barcode-scanner";
import { Camera } from "expo-camera";
export default function Scanner() {
const [hasPermission, setHasPermission] = useState(null);
const [scanned, setScanned] = useState(false);
useEffect(() => {
(async () => {
const { status } = await BarCodeScanner.requestPermissionsAsync();
setHasPermission(status === "granted");
})();
}, []);
const handleBarCodeScanned = ({ type, data }) => {
setScanned(true);
alert(`Bar code with type ${type} and data ${data} has been scanned!`);
};
if (hasPermission === null) {
return <Text>Requesting for camera permission</Text>;
}
if (hasPermission === false) {
return <Text>No access to camera</Text>;
}
return (
<View style={styles.container}>
<Camera
style={StyleSheet.absoluteFillObject}
onBarCodeScanned={console.log}
barCodeScannerSettings={{
barCodeTypes: [BarCodeScanner.Constants.BarCodeType.qr],
}}
/>
{scanned && (
<Button title={"Tap to Scan Again"} onPress={() => setScanned(false)} />
)}
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: "column",
justifyContent: "center",
},
});
But in localhost I have the message "no access to camera"and I have no popup asking for access.
I am a complete beginner with react native and I can't figure out how to fix this ^^.
EDIT : I'm running it on web

Invalid hook call. Hooks can only be called inside of the body of a function component. : barcode-scanner

I try to use the barcode-scanner on my react native app, but a I get this error :
Invalid hook call. Hooks can only be called inside of the body of a function component.
That is the code source of my component :
import React, { useState, useEffect, Component } from 'react';
import { Text, View, StyleSheet, Button } from 'react-native';
import { BarCodeScanner } from 'expo-barcode-scanner';
class QrCode extends Component {
render () {
const [hasPermission, setHasPermission] = useState(null);
const [scanned, setScanned] = useState(false);
useEffect(() => {
(async () => {
const { status } = await BarCodeScanner.requestPermissionsAsync();
setHasPermission(status === 'granted');
})();
}, []);
const handleBarCodeScanned = ({ type, data }) => {
setScanned(true);
alert(`Bar code with type ${type} and data ${data} has been scanned!`);
};
if (hasPermission === null) {
return <Text>Requesting for camera permission</Text>;
}
if (hasPermission === false) {
return <Text>No access to camera</Text>;
}
return (
<View style={styles.container}>
<BarCodeScanner
onBarCodeScanned={scanned ? undefined : handleBarCodeScanned}
style={StyleSheet.absoluteFillObject}
/>
{scanned && <Button title={'Tap to Scan Again'} onPress={() => setScanned(false)} />}
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center'
}
});
export default QrCode;

Render after fetching async data

I am fetching data in useEffect() and then modifying the object (modifying clients.unreadMessages based on what should render icon), which is later sent to the component to render. But this component does not render correctly always, the icon is sometimes missing. I think it's because data are modified after rendering.
ClientList
import Colors from '#helper/Colors';
import { useSelector } from '#redux/index';
import { HealthierLifeOption } from '#redux/types';
import React, { useEffect, useState } from 'react';
import { View, Text, FlatList, ActivityIndicator } from 'react-native';
import ClientItem from './ClientItem';
import {usePubNub} from "pubnub-react";
export type Client = {
id: number;
username: string;
individualPlanPaid: boolean;
healthierLifeOption?: HealthierLifeOption;
company?: {
id: number;
companyName: string;
};
mentor?: {
id: number;
username: string;
};
}
type Props = {
navigation: any;
clients: Client[];
isAdmin?: boolean;
isLoading: boolean;
onEndReached: Function;
fromAdminTab?: boolean
}
const ClientList = ({ navigation, clients, onEndReached, isLoading, isAdmin = false, fromAdminTab= false }: Props) => {
const resultCount = useSelector(state => state.user.menteesResultCount);
const [hasMoreResults, setHasMoreResults] = useState(true);
const userId = useSelector(state => state.user.id);
const pubnub = usePubNub();
useEffect(() => {
setHasMoreResults(resultCount !== clients?.length);
}, [resultCount, clients]);
useEffect(() => {
getUnreadMessagesProccess().then(r => console.log("aaaaaaaaa"));
}, []);
async function setGrant() {
return new Promise( async resolve => {
await pubnub.grant({
channels: [userId.toString() + '.*'],
ttl: 55,
read: true,
write: true,
update: true,
get: true,
}, response => resolve(response));
});
}
async function getMetadata() {
const options = {include: {customFields: true}};
return await pubnub.objects.getAllChannelMetadata(options);
}
function setChannelsAndTokens(channelMetadata, channels, tokens) {
channelMetadata.data.forEach((value, index) => {
tokens.push(value.custom.lastToken);
channels.push(value.id)
});
}
async function getUnreadedMessages(channels, tokens) {
return await pubnub.messageCounts({
channels: channels,
channelTimetokens: tokens,
});
}
async function getUnreadMessagesProccess() {
const tokens = ['1000'];
const channels = ['1234567'];
const auth = await setGrant();
const channelMetadata = await getMetadata();
const l = await setChannelsAndTokens(channelMetadata, channels, tokens);
const unread = await getUnreadedMessages(channels, tokens).then((res) => {
clients.forEach((value, index) => {
if (res.channels[value.id + '-' + userId + '-chat']) {
value.unreadMessages = res["channels"][value.id + '-' + userId + '-chat'];
} else {
value.unreadMessages = 0;
}
})
console.log(res);
});
return unread;
}
return (
<View>
<FlatList
keyExtractor={item => item.id.toString()}
data={clients}
onEndReached={() => hasMoreResults ? onEndReached() : null}
onEndReachedThreshold={0.4}
renderItem={item => (
<ClientItem
client={item.item}
isAdmin={isAdmin}
navigation={navigation}
fromAdminTab={fromAdminTab}
/>
)}
ListFooterComponent={isLoading
? () => (
<View style={{ padding: 20 }}>
<ActivityIndicator animating size="large" color={Colors.border_gray} />
</View>
)
: null
}
/>
</View>
);
}
export default ClientList;
ClientItem
import React, {useEffect, useState} from 'react';
import { Text, View, Image, TouchableOpacity } from 'react-native';
import Images from '#helper/Images';
import FontAwesome5 from 'react-native-vector-icons/FontAwesome5';
import Colors from '#helper/Colors';
import styles from '../styles';
import ClientModal from './ClientModal/ClientModal';
import { Client } from './ClientList';
import { HealthierLifeOption } from '#redux/types';
type Props = {
client: Client;
navigation: any;
isAdmin?: boolean;
fromAdminTab?: boolean
}
const ClientItem = ({ client, navigation, isAdmin = false, fromAdminTab= false }: Props) => {
const [showModal, setShowModal] = useState(false);
const [showIcon, setShowIcon] = useState(false)
console.log(client.unreadMessages)
useEffect(() =>{
if(client.unreadMessages>0)
setShowIcon(true);
},[client.unreadMessages]);
let clientIcon = Images.icon.logoIcon;
const handleContinueButton = () => {
if(!fromAdminTab) {
navigation.navigate('ChatFromClientsList', { selectedId: client.id, showBackButton: true });
}else {
setShowModal(!showModal)
}
};
return (
<View style={styles.client_item_container}>
<TouchableOpacity style={styles.client_content_container} onPress={handleContinueButton}
>
<View style={styles.image_container}>
<Image
style={styles.client_profile_img}
source={clientIcon}
resizeMode="contain"
resizeMethod="resize"
/>
</View>
<View style={styles.title_container}>
<Text style={styles.title} >{ client.username } </Text>
</View>
<View style={styles.dot_container}>
{showIcon && <FontAwesome5
name="comment-dots"
size={20}
color={Colors.red}
/> }
</View>
<View style={styles.hamburger_container} >
<FontAwesome5
name="bars"
size={30}
color={Colors.black}
onPress={() => setShowModal(!showModal)}
/>
</View>
<View>
<ClientModal
isVisible={showModal}
onCollapse={(value: boolean) => setShowModal(value)}
closeModal={(value: boolean) => setShowModal(value)}
client={client}
navigation={navigation}
isAdmin={isAdmin}
/>
</View>
</TouchableOpacity>
</View>
);
};
export default ClientItem;
This code does not render correctly always:
{showIcon && <FontAwesome5
name="comment-dots"
size={20}
color={Colors.red}
/> }
You should not calculate the value in renderItem.
Why you don’t calc the value outside of renderItem in pass it in
useEffect(() =>{
if(client.unreadMessages>0)
setShowIcon(true);
},[client.unreadMessages]);
Do something like:
renderItem={item => (
<ClientItem
client={item.item}
isAdmin={isAdmin}
navigation={navigation}
fromAdminTab={fromAdminTab}
showIcon={item.item.unreadMessages>0}
/>
)}
By the way renderItem should not be a anonymous function.

Change screen without a click event using navigation stack react native

Well what I'm trying to do is when he finishes reading the qr code is to move to the next screen as soon as this event ends. I tried to do this by declaring:
const handleBarCodeScanned = ({ type, data }) => {
{this.props.navigation.navigate ('testScreen', {data1, data2})}
}
Usually, the documentation always shows accompanied by an onClick () function associated with a button.
import React, { useState, useEffect } from 'react';
import { Text, View, StyleSheet, Button, PermissionsAndroid } from 'react-native';
import { BarCodeScanner } from 'expo-barcode-scanner';
import wifi from 'react-native-android-wifi';
export default function QrCodeScreen() {
const [hasPermission, setHasPermission] = useState(null);
const [scanned, setScanned] = useState(false);
useEffect(() => {
(async () => {
const { status } = await BarCodeScanner.requestPermissionsAsync();
setHasPermission(status === 'granted');
})();
}, []);
const handleBarCodeScanned = ({ type, data }) => {
{this.props.navigation.navigate('nextScreen', { data1, data2 })}//Change screen
})}
};
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',
justifyContent: 'flex-end',
}}>
<BarCodeScanner
onBarCodeScanned={scanned ? undefined : handleBarCodeScanned}
style={StyleSheet.absoluteFillObject}
/>
{scanned && <Button title={'Tap to Scan Again'} onPress={() => setScanned(false)} />}
</View>
);
}
Seems like you're using functional components so there is no this context.
You forget to import and init the navigation hook
import { useNavigation } from '#react-navigation/native';
And
export default function QrCodeScreen() {
const navigation = useNavigation();
...
Then
const handleBarCodeScanned = ({ type, data }) => {
navigation.navigate('nextScreen', { data1, data2 })
})}
I managed to solve the error by passing as the navigation parameter in the function declaration.
Before
export default function QrCodeScreen() {
}
After
export default function QrCodeScreen({navigation}) {
}
Change screen
navigation.navigate('SetupConnectionScreen');

React Native SectionList not reliably rendering one of its sections

I have a custom SectionList component, called EventSectionList:
import React from 'react';
import { SectionList } from 'react-native';
import { View, Text, Fab } from '../../../common/Layout';
import EventCard from '../EventCard';
import styles from './styles';
const EventSectionList = ({
sections,
refreshing,
onRefresh,
onEndReached,
onFabPress,
inExtraData,
}) => {
const renderSectionHeader = ({ section: { title } }) => (
<Text style={styles.sectionHeader}>{title}</Text>
);
const renderSectionFooter = () => <View style={styles.sectionSeparator} />;
const renderSeparator = () => <View style={styles.itemSeparator} />;
const renderFooter = () => <View style={styles.footer} />;
const renderEvent = event => {
const { item } = event;
return <EventCard event={item} />;
};
return (
<View style={styles.container}>
<SectionList
sections={sections}
refreshing={refreshing}
onRefresh={onRefresh}
onEndReachedThreshold={0}
onEndReached={onEndReached}
renderItem={renderEvent}
renderSectionHeader={renderSectionHeader}
renderSectionFooter={renderSectionFooter}
ItemSeparatorComponent={renderSeparator}
ListFooterComponent={renderFooter}
showsVerticalScrollIndicator={false}
extraData={inExtraData}
keyExtractor={(item, index) => String(index)}
/>
<Fab icon="add" style={styles.fabAdd} onPress={onFabPress} />
</View>
);
};
export default EventSectionList;
An EventSectionList is being rendered here on the EventsScreen:
import React, { useState, useEffect, Fragment } from 'react';
import { isEmpty } from 'lodash';
import { Text } from '../../../common/Layout';
import { connect } from './../../../common/Helpers';
import EventSectionList from './../../Events/EventSectionList';
import styles from './styles';
const EventsScreen = ({ services }) => {
const { eventsService } = services;
const [hostedEvents, setHostedEvents] = useState([]);
const [registeredEvents, setRegisteredEvents] = useState([]);
const [eventSections, setEventSections] = useState(null);
const [refreshing, setRefreshing] = useState(false);
const [page, setPage] = useState(1);
useEffect(() => {
if (!isEmpty(eventSections)) {
setRefreshing(false);
}
}, [eventSections]);
useEffect(() => {
if (!isEmpty(hostedEvents) || !isEmpty(registeredEvents)) {
setEventSections([
{
title: 'Upcoming Hosted Events',
data: hostedEvents,
},
{
title: 'Upcoming Registered Events',
data: registeredEvents,
},
]);
}
}, [hostedEvents, registeredEvents]);
useEffect(() => {
const hostedEventsPromise = async () => {
const { data } = await eventsService.getEvents({
page,
hosted: true,
upcoming: true,
page_size: 100,
});
if (page === 1) {
setHostedEvents(data);
} else {
setHostedEvents([...hostedEvents, ...data]);
}
};
const registeredEventsPromise = async () => {
const { data } = await eventsService.getEvents({
page,
hosted: false,
upcoming: true,
page_size: 100,
});
if (page === 1) {
setRegisteredEvents(data);
} else {
setRegisteredEvents([...registeredEvents, ...data]);
}
};
registeredEventsPromise();
hostedEventsPromise();
}, [page]);
const eventsRefresh = async () => {
if (!refreshing) {
setPage(1);
}
};
const eventsRefreshEnd = async () => {
if (!refreshing) {
setPage(page + 1);
}
};
return (
<Fragment>
{eventSections ? (
<EventSectionList
sections={eventSections}
inExtraData={registeredEvents}
refreshing={refreshing}
onRefresh={eventsRefresh}
onEndReached={eventsRefreshEnd}
onFabPress={() => {}}
/>
) : (
<Text style={styles.message}>You have no upcoming events.</Text>
)}
</Fragment>
);
};
export default connect()(EventsScreen);
Unfortunately, sometimes - the Registered Events section doesn't render, until the pull - down refresh occurs, and even then - it doesn't always render. I can see this section sort of trying to render - the EventCards flicker where they should be rendered, but they don't "stick".
For reference, here is the EventCard component:
import React, { useState } from 'react';
import { isEmpty } from 'lodash';
import moment from 'moment';
import { connect } from '../../../common/Helpers';
import { View, Text, Touchable } from '../../../common/Layout';
import { Image } from '../../../common/Image';
import styles from './styles';
const EventCard = ({ event }) => {
const dateFormat = 'MMM D';
const timeFormat = 'h:mm A';
const [startDate] = useState(
moment(event.start_date)
.format(dateFormat)
.toUpperCase()
);
const [startTime] = useState(moment(event.start_date).format(timeFormat));
const [startDateTime] = useState(`${startDate} AT ${startTime}`);
const [name] = useState(event.name);
const [address] = useState(
event.start_location && event.start_location.address
);
const [uri] = useState(event.cover_image);
return (
<Touchable>
<View style={styles.container}>
{!isEmpty(uri) ? (
<Image source={{ uri }} style={styles.detailsImage} />
) : (
<Image name="eventPlaceholder" style={styles.detailsImage} />
)}
<View style={styles.detailsContainer}>
<Text style={styles.detailsDate}>{startDateTime}</Text>
<Text style={styles.detailsName}>{name}</Text>
<Text style={styles.detailsAddress}>{address}</Text>
</View>
</View>
</Touchable>
);
};
export default connect()(EventCard);