React: Layout for embedded map and search cards - javascript

Currently I have the following layout in in react web page:
The map is an embedded component from an external site. The cards are filtered from a list and it renders the matching items. What I want to do is to make the cards appear on the right of the map instead of below. I've tried with a variety of css and div tricks but can't seem to make it work. Any suggestions are welcome.
Code:
import React, { useState, useEffect } from "react";
import axios from "axios";
import { Input } from "semantic-ui-react";
import BasicCard from "../components/BasicCard";
import { makeStyles } from "#material-ui/core/styles";
const useStyles = makeStyles((theme) => ({
root: {
display: "fixed",
marginTop: "15vh",
paddingLeft: "5vh",
width: "50vh",
},
map: {
marginTop: "5vh",
display: "block",
justifyContent: "left",
alignContent: "left",
},
cardHolder: {
display: "inline",
paddingLeft: "50vh",
justifyContent: "right",
justifyItems: "right",
alignContent: "right",
alignItems: "right",
},
searchBox: {
display: "block",
height: 1,
justifyContent: "left",
justifyItems: "left",
alignContent: "left",
alignItems: "left",
},
}));
export default function Posts() {
const [APIData, setAPIData] = useState([]);
const [filteredResults, setFilteredResults] = useState([]);
const [searchInput, setSearchInput] = useState("");
const classes = useStyles();
useEffect(() => {
const delayDebounceFn = setTimeout(() => {
console.log(searchInput);
searchItems(searchInput);
}, 1000);
axios
.get(
`https://data.gov.sg/api/action/datastore_search?resource_id=cdab7435-7bf0-4fa4-a8bd-6cfd231ca73a&limit=1000`
)
.then((response) => {
setAPIData(response.data.result.records);
console.log(response.data.result.records);
});
return () => clearTimeout(delayDebounceFn);
}, [searchInput]);
const sleep = (time) => {
return new Promise((resolve) => setTimeout(resolve, time));
};
const searchItems = (searchValue) => {
setSearchInput(searchValue);
if (searchInput !== "") {
const filteredData = APIData.filter((item) => {
console.log(Object.values(item).join("").toLowerCase());
return Object.values(item)
.join("")
.toLowerCase()
.includes(searchInput.toLowerCase());
});
setFilteredResults(filteredData);
} else {
setFilteredResults(APIData);
}
};
return (
<div className={classes.root}>
<Input
icon='search'
placeholder='Search...'
onChange={(e) => setSearchInput(e.target.value)}
className={classes.searchBox}
/>
<iframe
className={classes.map}
width='700'
height='400'
src='https://data.gov.sg/dataset/list-of-verified-public-access-aed-locations/resource/b0f395af-d297-4c22-8523-23fff99b17a7/view/3ea52d19-937b-4b2a-9da0-7b74e1d72005'
frameBorder='10'
>
{" "}
</iframe>
{searchInput.length > 1
? filteredResults.map((item) => {
return (
<BasicCard
variant='outlined'
raised={true}
sx={{
border: 1,
gap: 1,
borderColor: "text.primary",
alignItems: "right",
alignContent: "right",
}}
roadName={item.road_name}
postalCode={item.postal_code}
locationDesc={item.aed_location_description}
floorLevel={item.aed_location_description}
operatingHours={item.aed_location_floor_level}
></BasicCard>
);
})
: APIData.map((item) => {
return;
aaaaa;
})}
</div>
);
}

Related

SOLVED Expo-Camera, takePictureAsync undefined (Unhandled promise rejection: TypeError: ref.current.takePictureAsync is not a function)

I'm trying to create react app with expo using expo-camera for taking pictures. I have separately components MeasurementCameraScreen and MeasurementCamera. I'm using useRef() hook to be able to call takePictureAsync() from the MeasuremenCameraScreen.
When pressing the take image -button takePicture() console.logs the ref, so I assume the onPress gets there, but then I get the following error message:
[Unhandled promise rejection: TypeError: ref.current.takePictureAsync is not a function. (In 'ref.current.takePictureAsync(options)', 'ref.current.takePictureAsync' is undefined)]
I saw that people have also had same issues with takePictureAcync(), but I haven't found solution to my problem. I also tired to combine the MeasurementCameraScreen and MeasurementCamera components to one component, and with that I got the camera working, but I'm curious of why it doesn't work now? refs are new thing for me so I think there is something wrong with them.
Here are the components:
MeasurementCameraScreen
import { useRef } from 'react'
import { StyleSheet, TouchableOpacity, View } from 'react-native'
import MeasurementCamera from '../components/MeasurementCamera'
import Text from '../components/Text'
const MeasurementCameraScreen = () => {
const cameraRef = useRef(null)
return (
<View style={styles.container}>
<View style={styles.cameraContainer}>
<MeasurementCamera ref={cameraRef}/>
</View>
<View>
</View>
<TouchableOpacity
onPress={() => cameraRef.current.takePicture()}
style={styles.buttonContainer}
>
<Text>
Take image
</Text>
</TouchableOpacity>
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
cameraContainer: {
flex: 1,
},
buttonContainer: {
width: '100%',
height: 70,
backgroundColor: 'white',
justifyContent: 'center',
alignItems: 'center',
alignSelf: 'flex-end'
},
})
export default MeasurementCameraScreen
MeasurementCamera
import { useState, useEffect, useImperativeHandle, forwardRef } from 'react'
import { StyleSheet } from "react-native"
import { Camera } from 'expo-camera'
import Text from './Text'
const MeasurementCamera = forwardRef((props, ref) => {
const [hasPermission, setHasPermission] = useState(null)
useEffect(() => {
const getPermission = async () => {
const { status } = await Camera.requestCameraPermissionsAsync()
setHasPermission(status === 'granted')
}
getPermission()
}, [])
const takePicture = async () => {
if (ref) {
console.log(ref.current)
const options = {
quality: 1,
base64: true
}
const picture = await ref.current.takePictureAsync(options)
console.log(picture.uri)
}
}
useImperativeHandle(ref, () => ({
takePicture
}))
if (hasPermission === null) {
return <Text>Requesting for camera permission</Text>
} if (hasPermission === false) {
return <Text>No access to camera</Text>
}
return (
<Camera
ref={ref}
style={StyleSheet.absoluteFillObject}
/>
)
})
MeasurementCamera.displayName = 'MeasurementCamera'
export default MeasurementCamera
EDIT: Solved the problem, check out the comment for solution! :)
Okay I found the solution!
After reading the medium article #LouaySleman recommended about the expo-camera I understood that to be able to use the Expo Camera components functions I need to use ref. So what I needed was two refs, one for the components to communicate and another one to be able to use the Camera takePictureAsync() function.
Now that we have permissions to access the Camera, you should get familiar with the ref props on line 132, in the Camera component. There we have passed the cameraRef that was previously defined with useRef. In doing so, we will have access to interesting methods that we can call to control the Camera.
What you needed is to add two refs, one for the components to communicate and another one to be able to use the Camera takePictureAsync() function Please check this example from medium:
import React, { useState, useRef, useEffect } from "react";
import {
View,
Text,
TouchableOpacity,
SafeAreaView,
StyleSheet,
Dimensions,
} from "react-native";
import { Camera } from "expo-camera";
import { Video } from "expo-av";
export default function CameraScreen() {
const [hasPermission, setHasPermission] = useState(null);
const [cameraType, setCameraType] = useState(Camera.Constants.Type.back);
const [isPreview, setIsPreview] = useState(false);
const [isCameraReady, setIsCameraReady] = useState(false);
const [isVideoRecording, setIsVideoRecording] = useState(false);
const [videoSource, setVideoSource] = useState(null);
const cameraRef = useRef();
useEffect(() => {
(async () => {
const { status } = await Camera.requestPermissionsAsync();
setHasPermission(status === "granted");
})();
}, []);
const onCameraReady = () => {
setIsCameraReady(true);
};
const takePicture = async () => {
if (cameraRef.current) {
const options = { quality: 0.5, base64: true, skipProcessing: true };
const data = await cameraRef.current.takePictureAsync(options);
const source = data.uri;
if (source) {
await cameraRef.current.pausePreview();
setIsPreview(true);
console.log("picture", source);
}
}
};
const recordVideo = async () => {
if (cameraRef.current) {
try {
const videoRecordPromise = cameraRef.current.recordAsync();
if (videoRecordPromise) {
setIsVideoRecording(true);
const data = await videoRecordPromise;
const source = data.uri;
if (source) {
setIsPreview(true);
console.log("video source", source);
setVideoSource(source);
}
}
} catch (error) {
console.warn(error);
}
}
};
const stopVideoRecording = () => {
if (cameraRef.current) {
setIsPreview(false);
setIsVideoRecording(false);
cameraRef.current.stopRecording();
}
};
const switchCamera = () => {
if (isPreview) {
return;
}
setCameraType((prevCameraType) =>
prevCameraType === Camera.Constants.Type.back
? Camera.Constants.Type.front
: Camera.Constants.Type.back
);
};
const cancelPreview = async () => {
await cameraRef.current.resumePreview();
setIsPreview(false);
setVideoSource(null);
};
const renderCancelPreviewButton = () => (
<TouchableOpacity onPress={cancelPreview} style={styles.closeButton}>
<View style={[styles.closeCross, { transform: [{ rotate: "45deg" }] }]} />
<View
style={[styles.closeCross, { transform: [{ rotate: "-45deg" }] }]}
/>
</TouchableOpacity>
);
const renderVideoPlayer = () => (
<Video
source={{ uri: videoSource }}
shouldPlay={true}
style={styles.media}
/>
);
const renderVideoRecordIndicator = () => (
<View style={styles.recordIndicatorContainer}>
<View style={styles.recordDot} />
<Text style={styles.recordTitle}>{"Recording..."}</Text>
</View>
);
const renderCaptureControl = () => (
<View style={styles.control}>
<TouchableOpacity disabled={!isCameraReady} onPress={switchCamera}>
<Text style={styles.text}>{"Flip"}</Text>
</TouchableOpacity>
<TouchableOpacity
activeOpacity={0.7}
disabled={!isCameraReady}
onLongPress={recordVideo}
onPressOut={stopVideoRecording}
onPress={takePicture}
style={styles.capture}
/>
</View>
);
if (hasPermission === null) {
return <View />;
}
if (hasPermission === false) {
return <Text style={styles.text}>No access to camera</Text>;
}
return (
<SafeAreaView style={styles.container}>
<Camera
ref={cameraRef}
style={styles.container}
type={cameraType}
flashMode={Camera.Constants.FlashMode.on}
onCameraReady={onCameraReady}
onMountError={(error) => {
console.log("camera error", error);
}}
/>
<View style={styles.container}>
{isVideoRecording && renderVideoRecordIndicator()}
{videoSource && renderVideoPlayer()}
{isPreview && renderCancelPreviewButton()}
{!videoSource && !isPreview && renderCaptureControl()}
</View>
</SafeAreaView>
);
}
const WINDOW_HEIGHT = Dimensions.get("window").height;
const closeButtonSize = Math.floor(WINDOW_HEIGHT * 0.032);
const captureSize = Math.floor(WINDOW_HEIGHT * 0.09);
const styles = StyleSheet.create({
container: {
...StyleSheet.absoluteFillObject,
},
closeButton: {
position: "absolute",
top: 35,
left: 15,
height: closeButtonSize,
width: closeButtonSize,
borderRadius: Math.floor(closeButtonSize / 2),
justifyContent: "center",
alignItems: "center",
backgroundColor: "#c4c5c4",
opacity: 0.7,
zIndex: 2,
},
media: {
...StyleSheet.absoluteFillObject,
},
closeCross: {
width: "68%",
height: 1,
backgroundColor: "black",
},
control: {
position: "absolute",
flexDirection: "row",
bottom: 38,
width: "100%",
alignItems: "center",
justifyContent: "center",
},
capture: {
backgroundColor: "#f5f6f5",
borderRadius: 5,
height: captureSize,
width: captureSize,
borderRadius: Math.floor(captureSize / 2),
marginHorizontal: 31,
},
recordIndicatorContainer: {
flexDirection: "row",
position: "absolute",
top: 25,
alignSelf: "center",
justifyContent: "center",
alignItems: "center",
backgroundColor: "transparent",
opacity: 0.7,
},
recordTitle: {
fontSize: 14,
color: "#ffffff",
textAlign: "center",
},
recordDot: {
borderRadius: 3,
height: 6,
width: 6,
backgroundColor: "#ff0000",
marginHorizontal: 5,
},
text: {
color: "#fff",
},
});

React Native, passing data between screens when using BottomTabsNavigator

I am trying to make a react native application. I am using Bottom Tab Navigation, and I have 2 screens. On the first screen, a qr code is first scanned, it contains a string, which is written on the screen.
Then I would like to write out the afformentioned string on the second tab.
How can I pass this data between the screens of the Bottom Tab Navigator?
App:
import { StatusBar } from 'expo-status-bar';
import { StyleSheet, Text, View } from 'react-native';
import React from 'react';
import { NavigationContainer } from '#react-navigation/native';
import { createBottomTabNavigator } from '#react-navigation/bottom-tabs';
import QrcodeScreen from './src/screens/QrcodeScreen';
import EttermekScreen from './src/screens/EttermekScreen';
const Tab =createBottomTabNavigator();
export default function App() {
return (
<NavigationContainer>
<Tab.Navigator>
<Tab.Screen name='Qrcode' component={QrcodeScreen} />
<Tab.Screen name='Ettermek' component={EttermekScreen} />
</Tab.Navigator>
</NavigationContainer>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
QrcodeScreen:
import { StatusBar } from 'expo-status-bar';
import React, { useState, useEffect } from 'react';
import { Text, View, StyleSheet, Button } from 'react-native';
import { BarCodeScanner } from 'expo-barcode-scanner';
export default function QrcodeScreen() {
const [hasPermission, setHasPermission] = useState(null);
const [scanned, setScanned] = useState(false);
const [text, setText] = useState('Not yet scanned')
const askForCameraPermission = () => {
(async () => {
const { status } = await BarCodeScanner.requestPermissionsAsync();
setHasPermission(status === 'granted');
})()
}
// Request Camera Permission
useEffect(() => {
askForCameraPermission();
}, []);
// What happens when we scan the bar code
const handleBarCodeScanned = ({ type, data }) => {
setScanned(true);
setText(data)
console.log('Type: ' + type + '\nData: ' + data)
};
// Check permissions and return the screens
if (hasPermission === null) {
return (
<View style={styles.container}>
<Text>Requesting for camera permission</Text>
</View>)
}
if (hasPermission === false) {
return (
<View style={styles.container}>
<Text style={{ margin: 10 }}>No access to camera</Text>
<Button title={'Allow Camera'} onPress={() => askForCameraPermission()} />
</View>)
}
// Return the View
return (
<View style={styles.container}>
<View style={styles.barcodebox}>
<BarCodeScanner
onBarCodeScanned={scanned ? undefined : handleBarCodeScanned}
style={{ height: 400, width: 400 }} />
</View>
<Text style={styles.maintext}>{text}</Text>
{scanned && <Button title={'Scan again?'} onPress={() => setScanned(false)} color='tomato' />}
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
maintext: {
fontSize: 16,
margin: 20,
},
barcodebox: {
alignItems: 'center',
justifyContent: 'center',
height: 300,
width: 300,
overflow: 'hidden',
borderRadius: 30,
backgroundColor: 'tomato'
}
});
The screen I would like to print the string:
import { StyleSheet, Text, View } from 'react-native';
import React from 'react';
import { StatusBar } from 'expo-status-bar';
import QrcodeScreen from './QrcodeScreen';
export default function EttermekScreen({ route }) {
const { varisId } = route.params
return (
<View style={styles.container}>
<Text>ÉTTERMEK</Text>
<StatusBar style="auto" />
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
In EttermekScreen, you already have this line, const { varisId } = route.params, and grabbing from the props route, so you should be good with it.
Assuming you wanna send data in handleBarCodeScanned to EttermekScreen as a parameter called varisId, you could do it this way:
import { StatusBar } from 'expo-status-bar';
import React, { useState, useEffect } from 'react';
import { Text, View, StyleSheet, Button } from 'react-native';
import { BarCodeScanner } from 'expo-barcode-scanner';
export default function QrcodeScreen({navigation}) {
const [hasPermission, setHasPermission] = useState(null);
const [scanned, setScanned] = useState(false);
const [text, setText] = useState('Not yet scanned')
const askForCameraPermission = () => {
(async () => {
const { status } = await BarCodeScanner.requestPermissionsAsync();
setHasPermission(status === 'granted');
})()
}
// Request Camera Permission
useEffect(() => {
askForCameraPermission();
}, []);
// What happens when we scan the bar code
const handleBarCodeScanned = ({ type, data }) => {
setScanned(true);
setText(data);
navigation.navigate('Ettermek', {
varisId: data
});
};
// Check permissions and return the screens
if (hasPermission === null) {
return (
<View style={styles.container}>
<Text>Requesting for camera permission</Text>
</View>)
}
if (hasPermission === false) {
return (
<View style={styles.container}>
<Text style={{ margin: 10 }}>No access to camera</Text>
<Button title={'Allow Camera'} onPress={() => askForCameraPermission()} />
</View>)
}
// Return the View
return (
<View style={styles.container}>
<View style={styles.barcodebox}>
<BarCodeScanner
onBarCodeScanned={scanned ? undefined : handleBarCodeScanned}
style={{ height: 400, width: 400 }} />
</View>
<Text style={styles.maintext}>{text}</Text>
{scanned && <Button title={'Scan again?'} onPress={() => setScanned(false)} color='tomato' />}
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
maintext: {
fontSize: 16,
margin: 20,
},
barcodebox: {
alignItems: 'center',
justifyContent: 'center',
height: 300,
width: 300,
overflow: 'hidden',
borderRadius: 30,
backgroundColor: 'tomato'
}
});

Why is this MUI react data grid component rendered 4 times?

I have made a wrapper for MUI Data Grid Component as below:
Selection.tsx:
import * as React from 'react';
import { DataGrid, faIR, GridSelectionModel } from '#mui/x-data-grid';
import type {} from '#mui/x-data-grid/themeAugmentation';
import { createTheme, makeStyles, ThemeProvider } from '#mui/material/styles';
import { useRef, useState } from "react";
import { TablePagination } from "#mui/material";
import Stack from "#mui/material/Stack";
import DeleteIcon from '#mui/icons-material/Delete';
export default function ControlledSelectionServerPaginationGrid(props: any) {
const theme = createTheme(
{
breakpoints: {
values: {
xs: 300,
sm: 600,
md: 900,
lg: 1200,
xl: 1536
}
},
palette: {
primary: { main: '#1976d2' },
},
components: {
MuiDataGrid: {
styleOverrides: {
root: {
display: "flex",
flexDirection: "column-reverse",
borderRadius: '0',
paddingRight: '0px',
},
menu: {
direction: "rtl",
},
footerContainer: {
borderBottom: '1px solid #e0e0e0'
},
cell: {
textAlign: 'right',
'&:last-child': {
textAlign: 'center',
},
}
}
},
MuiTablePagination: {
styleOverrides: {
root: {
borderRadius: 'none',
direction: 'rtl',
},
displayedRows: {
margin: 0,
direction: 'ltr',
},
actions: {
direction: "ltr"
}
}
},
MuiToolbar: {
styleOverrides: {
root: {
display: 'flex',
flexDirection: 'row-reverse',
}
}
},
MuiTableCell: {
styleOverrides: {
root: {
border: 'none'
}
}
}
},
},
faIR,
);
const [page, setPage] = useState<number>(0);
const { packages, loading } = props.FetchData(page);
const [selected, setSelected] = useState<any>([]);
const [rowsPerPage, setRowsPerPage] = useState<number>(15);
const handleChangePage = (event: React.MouseEvent<HTMLButtonElement> | null, pageNumber: number) => {
setPage(pageNumber);
};
const handleChangeRowsPerPage = (
event: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>,
) => {
setRowsPerPage(parseInt(event.target.value));
setPage(0);
};
const CustomFooter = () => {
return (
<Stack
direction="row"
justifyContent="space-between"
alignItems="center"
sx={{
borderBottom: '1px solid #e0e0e0'
}}
>
<Stack
direction="row"
justifyContent="space-between"
alignItems="center"
sx={{
marginRight: '20px',
}}
>
{
selected && selected.length > 0 && (
<DeleteIcon sx={{color: "gray", cursor: 'pointer'}} onClick={() => alert('deleted them all')} />
)
}
</Stack>
<CustomPagination/>
</Stack>
)
}
const CustomPagination = () => {
return (
<TablePagination
count={packages?.meta?.last_page}
page={page}
onPageChange={handleChangePage}
rowsPerPage={rowsPerPage}
labelRowsPerPage={false}
onRowsPerPageChange={handleChangeRowsPerPage}
rowsPerPageOptions={[15, 30, 45, 60]}
/>
)
}
return (
<div style={{ height: '790px', width: '100%' }}>
<ThemeProvider theme={theme}>
{
loading ? (
<div>
loading
</div>
) : (
<DataGrid
rows={packages.data}
getRowId={(row) => row.uuid}
columns={props.columns}
pagination
components={{Footer: CustomFooter}}
checkboxSelection
disableSelectionOnClick
hideFooterSelectedRowCount
loading={loading}
onSelectionModelChange={(ids) => {
setSelected(ids);
}}
/>
)
}
</ThemeProvider>
</div>
);
}
I have the below hook as well which I'm passing to the above component as FetchData:
usePackages.tsx:
export default function usePackages(page: number) {
const [loading, setLoading] = useState<boolean>(false);
const [packages, setPackages] = useState<any>({
data: [],
meta: {},
links: {}
});
useEffect(() => {
setLoading(true);
getListOfAllPackages(page)
.then(data => {
setPackages({
...data.data,
});
setLoading(false);
})
.catch(error => {
setLoading(false);
});
}, [page]);
return { packages, loading };
Now the way I'm using the above components is just like below:
Packages.tsx:
import usePackages from "../../../hooks/Package/usePackages";
const configValues = (params: any) => {
return `${params.row.config.hdd}, ${params.row.config.cpu}, ${params.row.config.ram}`
}
const columns = [
{
field: "product_name",
headerName: "Product Name",
width: '200',
flex: 1
},
{
field: "name",
headerName: "Name",
width: '400',
flex: 1
},
{
field: "config",
headerName: "Config",
width: '150',
flex: 1,
valueGetter: configValues,
},
{
field: "description",
headerName: "Description",
width: '350',
flex: 1
},
{
field: 'actions',
type: 'Actions',
width: '100',
flex: 1,
},
]
return (
<div style={{ display: 'flex', height: '100%', marginTop: '64px'}}>
<div style={{ flexGrow: 1 }}>
<ControlledSelectionServerPaginationGrid columns={columns} FetchData={usePackages}/>
</div>
</div>
)
Now my Selection.tsx renders nearly 4 times and making the other components open with delay like modals and the Side Nav, I tried to console log everything and found out it's because of my passed hook props.FetchData that Selection.tsx re-renders itself, hence checking usePackages.tsx and finding out nothing is wrong. I've used such pattern previously and didn't confront such related issues.
I'm not sure the exact reason for these rerenders though. But I saw some potential things that may affect it.
First, I think it's better to put some variables like theme outside of your component body. Because on each rerender, this variable recreates.
Then, make sure to memorize some of the functions that you are using inside your component body like CustomPagination and CustomFooter with the useMemo hook. Because these are recreating with every rerender that happens, too.
After that, based on my experience, it's better to not pass functions through props. Because React can not compare functions for previous props and next props for each rerender. It's better to abstract functions and import them into the component file that you need.
Here, you can import usePackages directly from Selection.tsx instead of using it from props.
That's the idea behind hooks.

react native Warning: Cannot update a component from inside the function body of a different component

my code works but for some reason, I get this warning:
"Warning: Cannot update a component from inside the function body of a different component."
I think it is something to do with these lines (whan i add them i get the error):
const product = state.data.find((data) => data._id === id);
useEffect(() => {
if ((product.moistureSensor.tests !== undefined) || (product.lightSensor.tests !== undefined)) {
setDataState({ 'lightSensor': product.lightSensor.tests[product.lightSensor.tests.length - 1].status, 'muisterSensor': product.moistureSensor.tests[product.moistureSensor.tests.length - 1].status });
console.log(stateData);
}
}, []);
there is probably something logical in react that I don't understand.
this is the full code -
import React, { useContext, useEffect, useState, useRef } from 'react';
import {
Text,
StyleSheet,
Switch,
Button,
TouchableOpacity, SafeAreaView, View, ActivityIndicator
} from 'react-native';
import { client } from '../../api/SocketConfig'
import { NavigationEvents } from 'react-navigation';
import { Context as ProductDataContext } from '../../context/ProductDetailContext'
import DynamicProgressCircle from '../../components/DynamicProgressCircle';
const SensorDataShow = ({ id }) => {
const { state } = useContext(ProductDataContext);
const [isEnabled, setIsEnabled] = useState(false);
const [loading, setLoading] = useState(false);
const [stateData, setDataState] = useState({ 'lightSensor': null, 'muisterSensor': null });
const listener = (dataServer) => {
console.log(`the data from server:${dataServer}`);
if (dataServer.lightSensor !== null)
setDataState({ 'lightSensor': dataServer.lightSensor, 'muisterSensor': dataServer.muisterSensor });
setLoading(false)
}
const changeData = () => { client.on("showData", listener) }
const emitGetData = () => { client.emit("GetData", id) }
let timer = useRef(null);
useEffect(() => {
if (loading === true) {
console.log('timeout started')
timer.current = setTimeout(() => {
setLoading(false)
console.log('i am after timeout');
}, 35000);
} return () => clearTimeout(timer.current);
}, [loading]);
const product = state.data.find((data) => data._id === id);
useEffect(() => {
if ((product.moistureSensor.tests !== undefined) || (product.lightSensor.tests !== undefined)) {
setDataState({ 'lightSensor': product.lightSensor.tests[product.lightSensor.tests.length - 1].status, 'muisterSensor': product.moistureSensor.tests[product.moistureSensor.tests.length - 1].status });
console.log(stateData);
}
}, []);
useEffect(() => {
changeData();
return () => {
client.removeAllListeners('showData', listener);
}
}, []);
return (
<>
{stateData.muisterSensor !== null && (
<>
<Text style={{ fontSize: 28 }}>Moister sensor data:</Text>
<DynamicProgressCircle
changingValues={{
Percent: parseInt(
stateData.muisterSensor
),
}}
/>
</>
)}
{stateData.lightSensor !== null && (
<Text style={{ fontSize: 28 }}>
Light sensor data:
{
stateData.lightSensor
}
</Text>
)}
{loading === false ?
<Button
title="Get Data"
style={styles.button}
onPress={() => {
emitGetData(id), setLoading(true)
}}
/>
: <ActivityIndicator animating={true} size="large" color="red" />}
</>
);
};
const styles = StyleSheet.create({
button: {
alignItems: 'center',
backgroundColor: '#00CC00',
padding: 10,
marginTop: 10,
},
buttonON: {
alignItems: 'center',
backgroundColor: '#00CC00',
padding: 10,
marginTop: 10,
},
buttonOFF: {
alignItems: 'center',
backgroundColor: '#DB2828',
padding: 10,
marginTop: 10,
},
buttonNotOnline: {
alignItems: 'center',
backgroundColor: '#9B9D9A',
padding: 10,
marginTop: 10,
},
switch: {
alignItems: 'center',
},
});
export default SensorDataShow;
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
I appreciate any help I can get !

React Native Expo-Camera recording video find problem User rejected audio permission needed

I'm having trouble recording video with expo-camera. User rejected audio permission needed. Has anyone had such a problem?? If anyone tells me the way to solve the problem, I also added expo install expo-permissions. But it still doesn't work
taking snap working fine just problem through recoding video...Here is my code.
import React, { useState, useRef, useEffect } from "react";
import {
StyleSheet,
Dimensions,
View,
Text,
TouchableOpacity,
SafeAreaView,
} from "react-native";
import { Camera } from "expo-camera";
import { Video } from "expo-av";
const WINDOW_HEIGHT = Dimensions.get("window").height;
const closeButtonSize = Math.floor(WINDOW_HEIGHT * 0.032);
const captureSize = Math.floor(WINDOW_HEIGHT * 0.09);
export default function App() {
const [hasPermission, setHasPermission] = useState(null);
const [cameraType, setCameraType] = useState(Camera.Constants.Type.back);
const [isPreview, setIsPreview] = useState(false);
const [isCameraReady, setIsCameraReady] = useState(false);
const [isVideoRecording, setIsVideoRecording] = useState(false);
const [videoSource, setVideoSource] = useState(null);
const cameraRef = useRef();
useEffect(() => {
(async () => {
const { status } = await Camera.requestPermissionsAsync()
setHasPermission(status === "granted");
})();
}, []);
const onCameraReady = () => {
setIsCameraReady(true);
};
const takePicture = async () => {
if (cameraRef.current) {
const options = { quality: 0.5, base64: true, skipProcessing: true };
const data = await cameraRef.current.takePictureAsync(options);
const source = data.uri;
if (source) {
await cameraRef.current.pausePreview();
setIsPreview(true);
console.log("picture source", source);
}
}
};
const recordVideo = async () => {
if (cameraRef.current) {
try {
const videoRecordPromise = cameraRef.current.recordAsync();
if (videoRecordPromise) {
setIsVideoRecording(true);
const data = await videoRecordPromise;
const source = data.uri;
if (source) {
setIsPreview(true);
console.log("video source", source);
setVideoSource(source);
}
}
} catch (error) {
console.warn(error);
}
}
};
const stopVideoRecording = () => {
if (cameraRef.current) {
setIsPreview(false);
setIsVideoRecording(false);
cameraRef.current.stopRecording();
}
};
const switchCamera = () => {
if (isPreview) {
return;
}
setCameraType((prevCameraType) =>
prevCameraType === Camera.Constants.Type.back
? Camera.Constants.Type.front
: Camera.Constants.Type.back
);
};
const cancelPreview = async () => {
await cameraRef.current.resumePreview();
setIsPreview(false);
setVideoSource(null);
};
const renderCancelPreviewButton = () => (
<TouchableOpacity onPress={cancelPreview} style={styles.closeButton}>
<View style={[styles.closeCross, { transform: [{ rotate: "45deg" }] }]} />
<View
style={[styles.closeCross, { transform: [{ rotate: "-45deg" }] }]}
/>
</TouchableOpacity>
);
const renderVideoPlayer = () => (
<Video
source={{ uri: videoSource }}
shouldPlay={true}
style={styles.media}
/>
);
const renderVideoRecordIndicator = () => (
<View style={styles.recordIndicatorContainer}>
<View style={styles.recordDot} />
<Text style={styles.recordTitle}>{"Recording..."}</Text>
</View>
);
const renderCaptureControl = () => (
<View style={styles.control}>
<TouchableOpacity disabled={!isCameraReady} onPress={switchCamera}>
<Text style={styles.text}>{"Flip"}</Text>
</TouchableOpacity>
<TouchableOpacity
activeOpacity={0.7}
disabled={!isCameraReady}
onLongPress={recordVideo}
onPressOut={stopVideoRecording}
onPress={takePicture}
style={styles.capture}
/>
</View>
);
if (hasPermission === null) {
return <View />;
}
if (hasPermission === false) {
return <Text style={styles.text}>No access to camera</Text>;
}
return (
<SafeAreaView style={styles.container}>
<Camera
ref={cameraRef}
style={styles.container}
type={cameraType}
flashMode={Camera.Constants.FlashMode.on}
onCameraReady={onCameraReady}
onMountError={(error) => {
console.log("cammera error", error);
}}
/>
<View style={styles.container}>
{isVideoRecording && renderVideoRecordIndicator()}
{videoSource && renderVideoPlayer()}
{isPreview && renderCancelPreviewButton()}
{!videoSource && !isPreview && renderCaptureControl()}
</View>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
container: {
...StyleSheet.absoluteFillObject,
},
closeButton: {
position: "absolute",
top: 35,
left: 15,
height: closeButtonSize,
width: closeButtonSize,
borderRadius: Math.floor(closeButtonSize / 2),
justifyContent: "center",
alignItems: "center",
backgroundColor: "#c4c5c4",
opacity: 0.7,
zIndex: 2,
},
media: {
...StyleSheet.absoluteFillObject,
},
closeCross: {
width: "68%",
height: 1,
backgroundColor: "black",
},
control: {
position: "absolute",
flexDirection: "row",
bottom: 38,
width: "100%",
alignItems: "center",
justifyContent: "center",
},
capture: {
backgroundColor: "#f5f6f5",
borderRadius: 5,
height: captureSize,
width: captureSize,
borderRadius: Math.floor(captureSize / 2),
marginHorizontal: 31,
},
recordIndicatorContainer: {
flexDirection: "row",
position: "absolute",
top: 25,
alignSelf: "center",
justifyContent: "center",
alignItems: "center",
backgroundColor: "transparent",
opacity: 0.7,
},
recordTitle: {
fontSize: 14,
color: "#ffffff",
textAlign: "center",
},
recordDot: {
borderRadius: 3,
height: 6,
width: 6,
backgroundColor: "#ff0000",
marginHorizontal: 5,
},
text: {
color: "#fff",
},
});
Using expo-camera with expo-av you can do something like that:
const getPermissions = useCallback(
async () => {
const { status } = await Camera.requestPermissionsAsync();
await Audio.requestPermissionsAsync();
await Audio.setAudioModeAsync({
allowsRecordingIOS: true,
playsInSilentModeIOS: true,
});
setHasPermission(status === 'granted');
},
[sound, hasPermission],
)
Now in the expo camera, we have to ask for mic permission separately from the user. You can update your code like this:
const [hasCameraPermission, setHasCameraPermission] = useState(false);
const [hasMicPermission, setHasMicPermission] = useState(false);
useEffect(()=>{
(async () => {
const camStatus = await Camera.requestCameraPermissionsAsync();
if (camStatus.granted){
sethasCameraPermission(true)
}
const micStatus = await Camera.requestMicrophonePermissionsAsync();
if (micStatus.granted){
setHasMicPermission(true);}
})();
},[])

Categories

Resources