React Native doesn't update state - reactjs

I'm writing a simple animation and trying to reset it after it's complete
import React, { useState, useEffect } from 'react';
import { StyleSheet, View, Image, Animated, Easing } from 'react-native';
export default function App() {
const [leftPos, setLeftPos] = useState(new Animated.Value(0))
useEffect(() => {
cycleAnimation()
}, []);
const cycleAnimation = () => {
console.log('STARTING ANIMATION:', leftPos)
Animated.sequence([
Animated.timing(
leftPos,
{
toValue: 430,
duration: 3000,
easing: Easing.linear,
useNativeDriver: false
}
)
]).start(() => {
setLeftPos(new Animated.Value(0))
cycleAnimation()
})
}
return (
<View style={{ backgroundColor: 'black', flex: 1 }}>
<Animated.View style={{ left: leftPos }}>
<Image
style={styles.cloud}
source={require('./assets/cloud.png')}
/>
</Animated.View>
</View >
);
}
const styles = StyleSheet.create({
cloud: {
}
});
leftPos is always 430 (except for the first iteration) in cycleAnimation despite calling setLeftPos with new 0 value. Also tried putting cycleAnimation in setLeftPos's callback but got the same result.

You are resetting it incorrectly. No need to update state, try this:
leftPos.setValue(0);

Put your function inside useEffect
import React, { useState, useEffect } from "react";
import { StyleSheet, View, Image, Animated, Easing } from "react-native";
export default function App() {
const [leftPos, setLeftPos] = useState(new Animated.Value(0));
useEffect(() => {
const cycleAnimation = () => {
console.log("STARTING ANIMATION:", leftPos);
Animated.sequence([
Animated.timing(leftPos, {
toValue: 430,
duration: 3000,
easing: Easing.linear,
useNativeDriver: false,
}),
]).start(() => {
setLeftPos(new Animated.Value(0));
});
};
cycleAnimation();
}, [leftPos]);
return (
<View style={{ backgroundColor: "black", flex: 1 }}>
<Animated.View style={{ left: leftPos }}>
<Image style={styles.cloud} source={require("./assets/icon.png")} />
</Animated.View>
</View>
);
}
const styles = StyleSheet.create({
cloud: {},
});

Related

REACT NATIVE: fetching array of image from firestore database collection to carousel

Good day.
I have managed to fetch an array of images from firestore database collection, the only problem is that it seems that I cannot loop over the array that I am retrieving from the database.
I need to dynamically display my images on the carousel.
import React, { useState, useEffect, useCallback } from 'react';
import { Text, Dimensions, StyleSheet, View, Image } from 'react-native';
import { SwiperFlatList } from 'react-native-swiper-flatlist';
import firestore from '#react-native-firebase/firestore';
import { white } from 'react-native-paper/lib/typescript/styles/colors';
import { color } from 'react-native-reanimated';
import Carousel from 'react-native-reanimated-carousel';
//const colors = ["tomato", "thistle", "skyblue", "teal"];
const names = [{"AdImage": "https://firebasestorage.googleapis.com/v0/b/pindot-65e7c.appspot.com/o/Ads%2FJB.png?alt=media&token=377f1629-6807-4343-b826-93d1c2bc5de6"}, {"AdImage": "https://firebasestorage.googleapis.com/v0/b/pindot-65e7c.appspot.com/o/Ads%2Fgymtarp.png?alt=media&token=b2d1c923-2b5c-4066-bf8f-dc12de399059"}, {"AdImage": "https://firebasestorage.googleapis.com/v0/b/pindot-65e7c.appspot.com/o/photos%2Fb75d0848-b37f-4584-ab46-f355ca838e83.jpg?alt=media&token=adaa8559-de91-4ced-b056-84300123102e"}, {"AdImage": "https://firebasestorage.googleapis.com/v0/b/pindot-65e7c.appspot.com/o/Ads%2FSANDO.png?alt=media&token=274c946b-7f20-4c07-8545-431a0558257c"}];
const App = () => {
const [ads, setAds] = useState([]); // Initial empty array of ads
useEffect(() => {
const subscriber = firestore()
.collection('AdsDB')
//.orderBy('Menu', 'asc')
.onSnapshot(querySnapshot => {
//const ads = [];
/*
querySnapshot.forEach(documentSnapshot => {
ads.push({
...documentSnapshot.data(),
key: documentSnapshot.id,
});
});
*/
querySnapshot.forEach(doc => {
const { AdImage } = doc.data();
ads.push({
//id: doc.id,
AdImage,
//Price,
});
});
setAds(ads);
console.log(ads);
//console.log(Object.entries(ads));
});
// Unsubscribe from events when no longer in use
return () => subscriber();
}, []);
return (
<View style={styles.container}>
<SwiperFlatList
autoplay
autoplayDelay={2}
autoplayLoop
index={2}
showPagination
data={ads}
renderItem={({ item }) => (
<View style={[styles.child, { backgroundColor: item }]}>
<Text style={styles.text}>{item.AdImage}</Text>
<Image
style={{
width: "100%",
height: "30%",
position: 'absolute',
top:0,
alignItems: 'center',
justifyContent: 'center'}}
source={{uri : item.AdImage}}
resizeMode={'stretch'} // cover or contain its upto you view look
/>
</View>
)}
/>
</View>
)
};
const { width } = Dimensions.get('window');
const styles = StyleSheet.create({
container: { flex: 1, backgroundColor: 'white', },
child: { width, justifyContent: 'center', height: '100%' },
text: { fontSize: width * 0.1, textAlign: 'center' },
});
export default App;
Thank you for answering my question. Mabuhay! I'm from the Philippines.
The problem I am seeing is that probably meanwhile you are receiving asynchronously the snapshots, the setState did not ended running (a race condition between the velocity receiving snapshots and the exact time needed to persist the state)
I would try to delay the state change and use the previous state value in the setter, like this:
useEffect(() => {.
const subscriber = firestore()
.collection('AdsDB')
.onSnapshot(querySnapshot => {
querySnapshot.forEach(doc => {
const { AdImage } = doc.data();
setTimeout(() => {
setAds((prevAds) => [...prevAds, AdImage]);
}, 500);
});
console.log(ads);
//console.log(Object.entries(adsFromFirebase));
});
These are my code, Sir. It gives an empty array.
import React, { useState, useEffect, useCallback } from 'react';
import { Text, Dimensions, StyleSheet, View, Image } from 'react-native';
import { SwiperFlatList } from 'react-native-swiper-flatlist';
import firestore from '#react-native-firebase/firestore';
import { white } from 'react-native-paper/lib/typescript/styles/colors';
import { color } from 'react-native-reanimated';
const colors = ["tomato", "thistle", "skyblue", "teal"];
const names = [{"AdImage": "https://firebasestorage.googleapis.com/v0/b/pindot-65e7c.appspot.com/o/Ads%2FJB.png?alt=media&token=377f1629-6807-4343-b826-93d1c2bc5de6"}, {"AdImage": "https://firebasestorage.googleapis.com/v0/b/pindot-65e7c.appspot.com/o/Ads%2Fgymtarp.png?alt=media&token=b2d1c923-2b5c-4066-bf8f-dc12de399059"}, {"AdImage": "https://firebasestorage.googleapis.com/v0/b/pindot-65e7c.appspot.com/o/photos%2Fb75d0848-b37f-4584-ab46-f355ca838e83.jpg?alt=media&token=adaa8559-de91-4ced-b056-84300123102e"}, {"AdImage": "https://firebasestorage.googleapis.com/v0/b/pindot-65e7c.appspot.com/o/Ads%2FSANDO.png?alt=media&token=274c946b-7f20-4c07-8545-431a0558257c"}];
const App = () => {
const [ads, setAds] = useState([]); // Initial empty array of ads
useEffect(() => {
const subscriber = firestore()
.collection('AdsDB')
.onSnapshot(querySnapshot => {
querySnapshot.forEach(doc => {
const { AdImage } = doc.data();
setTimeout(() => {
setAds((prevAds) => [...prevAds, AdImage]);
}, 500);
});
console.log(ads);
//console.log(Object.entries(adsFromFirebase));
});
// Unsubscribe from events when no longer in use
return () => subscriber();
}, []);
/*
useEffect(() => {
const subscriber = firestore()
.collection('AdsDB')
//.orderBy('Menu', 'asc')
.onSnapshot(querySnapshot => {
//const ads = [];
/*
querySnapshot.forEach(documentSnapshot => {
ads.push({
...documentSnapshot.data(),
key: documentSnapshot.id,
});
});
querySnapshot.forEach(doc => {
const { AdImage } = doc.data();
ads.push({
//id: doc.id,
AdImage,
//Price,
});
});
setAds(ads);
//console.log(ads);
//console.log(Object.entries(ads));
});
// Unsubscribe from events when no longer in use
return () => subscriber();
}, []);
*/
return (
<View style={styles.container}>
<SwiperFlatList
autoplay
autoplayDelay={2}
autoplayLoop
index={2}
data={ads}
renderItem={({ item }) => (
<View style={[styles.child, { backgroundColor: item.colors }]}>
<Text style={styles.text}>{item.AdImage}</Text>
<Image
style={{
width: "100%",
height: "30%",
position: 'absolute',
top:0,
alignItems: 'center',
justifyContent: 'center'}}
source={{uri : item.AdImage}}
resizeMode={'stretch'} // cover or contain its upto you view look
/>
{/*}*/}
</View>
)}
/>
</View>
)
};
const { width } = Dimensions.get('window');
const styles = StyleSheet.create({
container: { flex: 1, backgroundColor: 'white', },
child: { width, justifyContent: 'center', height: '100%' },
text: { fontSize: width * 0.1, textAlign: 'center' },
});
export default App;
This is the output.
LOG Running "myApp" with {"rootTag":21}
LOG []

React Native Webview make back button on android go back

When I click the hardware button on android the app closes, I want to go back on the previous page,
This is my code
import { StatusBar } from 'expo-status-bar';
import React, { useState } from 'react';
import { ActivityIndicator, Linking, SafeAreaView, StyleSheet, Text, View } from 'react-native';
import { WebView } from 'react-native-webview';
export default function App() {
const [isLoadong, setLoading] = useState(false);
return (
<SafeAreaView style={styles.safeArea}>
<WebView
originWhiteList={['*']}
source={{ uri: 'https://google.com' }}
style={styles.container }
onLoadStart={(syntheticEvent) => {
setLoading(true);
}}
onShouldStartLoadWithRequest={(event)=>{
if (event.navigationType === 'click') {
if (!event.url.match(/(google\.com\/*)/) ) {
Linking.openURL(event.url)
return false
}
return true
}else{
return true;
}
}}
onLoadEnd={(syntheticEvent) => {
setLoading(false);
}} />
{isLoadong && (
<ActivityIndicator
color="#234356"
size="large"
style={styles.loading}
/>
)}
</SafeAreaView>
);
}
const styles = StyleSheet.create({
safeArea: {
flex: 1,
backgroundColor: '#234356'
},
loading: {
position: 'absolute',
left: 0,
right: 0,
top: 0,
bottom: 0,
alignItems: 'center',
justifyContent: 'center'
},
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
There are built-in goBack() method available in react-native-webview libraries
you can use the API method to implement back navigation of webview.
For this, you have to get the reference of react-native-webview component and call method from the reference object.
also, you are able to put the listener on the Android Native Back Button Press event to call the goBack() method of webview.
try the following code...
import { StatusBar } from 'expo-status-bar';
import React, { useState, useRef, useEffect } from 'react';
import { ActivityIndicator, Linking, SafeAreaView, StyleSheet, BackHandler } from 'react-native';
import { WebView } from 'react-native-webview';
export default function App() {
const webViewRef = useRef()
const [isLoadong, setLoading] = useState(false);
const handleBackButtonPress = () => {
try {
webViewRef.current?.goBack()
} catch (err) {
console.log("[handleBackButtonPress] Error : ", err.message)
}
}
useEffect(() => {
BackHandler.addEventListener("hardwareBackPress", handleBackButtonPress)
return () => {
BackHandler.removeEventListener("hardwareBackPress", handleBackButtonPress)
};
}, []);
return (
<SafeAreaView style={styles.safeArea}>
<WebView
originWhiteList={['*']}
source={{ uri: 'https://google.com' }}
style={styles.container}
ref={webViewRef}
onLoadStart={(syntheticEvent) => {
setLoading(true);
}}
onShouldStartLoadWithRequest={(event)=>{
if (event.navigationType === 'click') {
if (!event.url.match(/(google\.com\/*)/) ) {
Linking.openURL(event.url)
return false
}
return true
}
else{
return true;
}
}}
onLoadEnd={(syntheticEvent) => {
setLoading(false);
}}
/>
{isLoadong && (
<ActivityIndicator
color="#234356"
size="large"
style={styles.loading}
/>
)}
</SafeAreaView>
);
}
const styles = StyleSheet.create({
safeArea: {
flex: 1,
backgroundColor: '#234356'
},
loading: {
position: 'absolute',
left: 0,
right: 0,
top: 0,
bottom: 0,
alignItems: 'center',
justifyContent: 'center'
},
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
first add ref for access your webview like that:
<WebView
ref={WEBVIEW_REF}
then for access to Hardware Back Button you can use this:
import { BackHandler } from 'react-native';
constructor(props) {
super(props)
this.handleBackButtonClick = this.handleBackButtonClick.bind(this);
}
componentWillMount() {
BackHandler.addEventListener('hardwareBackPress', this.handleBackButtonClick);
}
componentWillUnmount() {
BackHandler.removeEventListener('hardwareBackPress', this.handleBackButtonClick);
}
handleBackButtonClick() {
this.refs[WEBVIEW_REF].goBack();
return true;
}
in handleBackButtonClick you can do back for webview and add this.refs[WEBVIEW_REF].goBack(); . I Hope that's helpful:)
Here is a simple solution using the magic of React's State.
Hope this helps.
import React, { useRef, useState } from 'react'
export default function Component () {
// This is used to save the reference of your webview, so you can control it
const webViewRef = useRef(null);
// This state saves whether your WebView can go back
const [webViewcanGoBack, setWebViewcanGoBack] = useState(false);
const goBack = () => {
// Getting the webview reference
const webView = webViewRef.current
if (webViewcanGoBack)
// Do stuff here if your webview can go back
else
// Do stuff here if your webview can't go back
}
return (
<WebView
source={{ uri: `Your URL` }}
ref={webViewRef}
javaScriptEnabled={true}
onLoadProgress={({ nativeEvent }) => {
// This function is called everytime your web view loads a page
// and here we change the state of can go back
setWebViewcanGoBack(nativeEvent.canGoBack)
}}
/>
)
}

React native reanimated v2 Scale + Opacity withRepeat

I am trying to implement scaling View with Opacity togather through React native reanimated v2, but not able to contol withRepeat ...
Below code is just Perform scaling withRepeat but not Opacity. How to control Opacity + Scaling of view withRepeat ... Want to apply scaling and Opacity both on view in loop/Repeat.
import React, { useState } from 'react';
import { View, TouchableWithoutFeedback } from 'react-native';
import Animated,
{ withRepeat, useSharedValue, interpolate, useAnimatedStyle, useDerivedValue, withTiming }
from 'react-native-reanimated'
import Styles from './Styles';
function LoopApp() {
const [state, setState] = useState(0);
const scaleAnimation = useSharedValue(1);
const animationOpacityView = useSharedValue(1);
scaleAnimation.value = withRepeat(withTiming(2.5, { duration: 2000 }), -1, true);
//animationOpacityView.value = withRepeat(0, -1, true);
const debug = useDerivedValue(() => {
// console.log(scaleAnimation.value);
return scaleAnimation.value;
});
const growingViewStyle = useAnimatedStyle(() => {
return {
transform: [{ scale: scaleAnimation.value }],
opacity: withTiming(animationOpacityView.value, {
duration: 1500
}, () => {
animationOpacityView.value = 0.99
})
};
});
return (
<View style={Styles.container}>
<Animated.View style={[Styles.viewStyle, growingViewStyle]} />
</View>
);
}
export default LoopApp;
Style.js
import {DevSettings, Dimensions, I18nManager} from 'react-native';
import Constants from '../../common/Constants';
const Screen = {
width: Dimensions.get('window').width,
height: Dimensions.get('window').height,
};
export default {
container: {
flex: 1,
justifyContent: "center",
alignItems: "center",
},
viewStyle: {
backgroundColor: '#19a35c',
width: Screen.width * 0.0364,
height: Screen.width * 0.0364,
borderRadius: 100,
},
};

React Native Expo: Camera in ViewPager

I am creating a ViewPager with a Camera inside in a View, when the ViewPager renders the Views everything is ok but then when the ViewPager change the page and get back to the Camera Page the Camera is not appearing again. How to solve this? There is a way to render the camera asynchronously?
This is my ViewPager:
import React from 'react';
import ViewPager from '#react-native-community/viewpager';
import { View } from 'react-native';
const Pager = ({
pages,
initalPage,
onPageSelected,
onPageScrollStateChanged,
onPageScroll
}) => {
return (
<ViewPager
style={{flex: 1}}
initialPage={initalPage}
onPageSelected={(e)=>onPageSelected && onPageSelected(e.nativeEvent)}
onPageScrollStateChanged={(e)=>onPageScrollStateChanged && onPageScrollStateChanged(e.nativeEvent)}
onPageScroll={(e)=>onPageScroll && onPageScroll(e.nativeEvent)}
>
{
pages.map((page,i)=>
<View key={i} style={{flex: 1}}>
{
page
}
</View>
)
}
</ViewPager>
)
}
export default Pager;
This is my Camera Page:
import React, { useEffect, Suspense, useState } from 'react';
import { StyleSheet, View } from 'react-native';
import { Camera } from 'expo-camera';
import { LinearGradient } from 'expo-linear-gradient';
import { color } from '../../utils';
const ScannerView = ({
isInView,
}) => {
let camera = null;
const [hasPermission, setHasPermission] = useState(null);
const [cameraRatio, setCameraRatio] = useState('1:1');
useEffect(()=>{
(async () => {
const { status } = await Camera.requestPermissionsAsync();
setHasPermission(status === 'granted');
})();
}, []);
const handleCameraReady = () => {
camera.getSupportedRatiosAsync().then((data)=>{
setCameraRatio(data[data.length-1]);
});
}
const handleBarcodeScanned = (data) => {
console.log(data);
}
const handleThis = (err) => {
console.log("EVENT",err.nativeEvent)
}
const renderCamera = (
<Camera
ref={ref=>{
camera=ref;
}}
style={styles.camera}
type={Camera.Constants.Type.back}
focusable={true}
ratio={cameraRatio}
onCameraReady={handleCameraReady}
onBarCodeScanned={handleBarcodeScanned}
onMountError={handleThis}
/>
)
return (
<View style={styles.container}>
{
(hasPermission) &&
<Suspense>
{renderCamera}
</Suspense>
}
<LinearGradient
colors={['transparent', color.neutral80]}
style={styles.gradient}
/>
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'black',
alignItems: 'stretch'
},
camera: {
flex: 1,
backgroundColor: 'red',
},
gradient: {
flex: 1,
height: 220,
position: 'absolute',
bottom: 0,
left: 0,
right: 0
}
})
export default ScannerView;

Create a loop with react-native-reanimated

I'm trying to create a Blinking function in an App using hooks and react-navigation 5 - No classes.
"react-native-reanimated" is new to me. I'm more familiar with Animated so this is why I need some help here. Thank you!
import React, { useState, useEffect, Component, useCallback } from "react";
import {
StyleSheet,
Text,
View
} from "react-native";
import Animated, { Easing } from "react-native-reanimated";
import { loop } from "react-native-redash";
function BlinkIt(props){
const [fadeAnim] = useState(new Animated.Value(0));
useEffect(() => {
Animated.set(
fadeAnim,
loop({
duration: 5000,
autoStart: true,
boomerang: true
})
)
}, []);
return (
<Animated.View // Special animatable View
style={{
...props.style,
opacity: fadeAnim
}}
>
{props.children}
</Animated.View>
);
}
export default function App() {
return (
<View style={styles.container}> <BlinkIt><Text>The text is blinking</Text></BlinkIt></View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
paddingTop: Constants.statusBarHeight,
backgroundColor: '#ecf0f1',
padding: 8,
},
});
I made a video tutorial on how to create loop animation with react-native-reanimated https://youtu.be/W82p3WfwxrA. Or check code listing below
import React, {useMemo, useState, useEffect} from 'react';
import {
TouchableWithoutFeedback,
Image,
StyleSheet,
Dimensions,
} from 'react-native';
import Animated, { Easing, stopClock } from 'react-native-reanimated';
const imageSize = {
width: 256,
height: 256,
};
const screenWidth = Dimensions.get('window').width;
const animatedWidth = screenWidth + imageSize.width;
const {
useCode,
block,
set,
Value,
Clock,
eq,
clockRunning,
not,
cond,
startClock,
timing,
interpolate,
and,
} = Animated;
const runTiming = (clock) => {
const state = {
finished: new Value(0),
position: new Value(0),
time: new Value(0),
frameTime: new Value(0),
};
const config = {
duration: 5000,
toValue: 1,
easing: Easing.inOut(Easing.linear),
};
return block([
// we run the step here that is going to update position
cond(
not(clockRunning(clock)),
set(state.time, 0),
timing(clock, state, config),
),
cond(eq(state.finished, 1), [
set(state.finished, 0),
set(state.position, 0),
set(state.frameTime, 0),
set(state.time, 0),
]),
state.position,
]);
}
export const AnimatedBackground = () => {
const [play, setPlay] = useState(false);
const {progress, clock, isPlaying} = useMemo(
() => ({
progress: new Value(0),
isPlaying: new Value(0),
clock: new Clock(),
}),
[],
);
useEffect(() => {
isPlaying.setValue(play ? 1 : 0);
}, [play, isPlaying]);
useCode(
() =>
block([
cond(and(not(clockRunning(clock)), eq(isPlaying, 1)), startClock(clock)),
cond(and(clockRunning(clock), eq(isPlaying, 0)), stopClock(clock)),
set(progress, runTiming(clock)),
]),
[progress, clock],
);
return (
<TouchableWithoutFeedback
style={styles.container}
onPress={() => setPlay(!play)}
>
<Animated.View style={[styles.image, { opacity: progress }]}>
<Image
style={styles.image}
source={require('./cloud.png')}
resizeMode="repeat"
/>
</Animated.View>
</TouchableWithoutFeedback>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
},
image: {
width: animatedWidth,
height: '100%',
},
});

Resources