Navigating to another screen while passing state to a button - reactjs

So I originally needed to pass states while navigating from a screen to another because I thought that would be enough to update a button as well as the screen iteself. However, the button that controlled the states is not being updated with the screen.
In the demo provided below(I included the code here as well) you can see that when navigating from Screen3, the state updates so that the red screen renders but the button at the top does not update as well.
How can I update the button along with the screen being updated?
I need to go from screen3 to the red screen while the button at the top shows we are on the red screen as well.
Here is the demo as well as the code below. Please keep in mind you must run the snack on IOS or android and you need to be running Expo Version 42(located in the bottom right of the screen)
Thank you for any insight at all! I appreciate it more than you know.
Home.js
import Slider from './components/slider'
const Home = ({ route }) => {
const [isVisile, setIsVisible] = React.useState(true);
const [whichComponentToShow, setComponentToShow] = React.useState("Screen1");
React.useEffect(() => {
if(route.params && route.params.componentToShow) {
setComponentToShow(route.params.componentToShow);
}
}, [route.params]);
const goToMap = () => {
setComponentToShow("Screen2");
}
const goToList = () => {
setComponentToShow("Screen1");
}
return(
<View style={{backgroundColor: '#d1cfcf' ,flex: 1}}>
{whichComponentToShow === 'Screen1' && <ListHome />}
{whichComponentToShow === 'Screen2' && <MapHome />}
<View style={{position: 'absolute', top: 0, left: 0, right: 1}}>
<Slider
renderMap={goToMap}
renderList={goToList}
/>
</View>
</View>
);
}
Screen3.js
const Screen3 = (props) => {
const navigation = useNavigation();
const onPress = () => {
navigation.navigate('Home', {
screen: 'Home',
params: {
componentToShow: 'Screen2'
}
});
}
return (
<View
style={{
flex: 1,
backgroundColor: 'white',
justifyContent: 'center',
alignItems: 'center',
}}>
<TouchableOpacity onPress={onPress}>
<Text style={{ color: 'black', fontSize: 25 }}>
Navigate to home and change to map screen
</Text>
</TouchableOpacity>
</View>
);
};
Finally Slider.js
const Slider = (props) => {
const [active, setActive] = useState(false)
let transformX = useRef(new Animated.Value(0)).current;
useEffect(() => {
if (active) {
Animated.timing(transformX, {
toValue: 1,
duration: 300,
useNativeDriver: true
}).start()
} else {
Animated.timing(transformX, {
toValue: 0,
duration: 300,
useNativeDriver: true
}).start()
}
}, [active]);
const rotationX = transformX.interpolate({
inputRange: [0, 1],
outputRange: [2, Dimensions.get('screen').width / 4]
})
return (
code for animation
)

Your Slider component needs to listen to screen focus with useFocusEffect and react accordingly. I added new props active to detect if the map screen is active.
Slider.js
import * as React from 'react';
import { useState, useEffect, useRef } from 'react'
import { View, Text, StyleSheet, Animated, TouchableOpacity, SafeAreaView, Dimensions, } from 'react-native';
import {
scale,
verticalScale,
moderateScale,
ScaledSheet,
} from 'react-native-size-matters';
import { useFocusEffect } from '#react-navigation/native';
const Slider = (props) => {
const [active, setActive] = useState(false)
let transformX = useRef(new Animated.Value(0)).current;
useFocusEffect( React.useCallback(()=>{
setActive(Boolean(props.active))
console.log()
},[props.active]))
useEffect(() => {
if (active) {
Animated.timing(transformX, {
toValue: 1,
duration: 300,
useNativeDriver: true
}).start()
} else {
Animated.timing(transformX, {
toValue: 0,
duration: 300,
useNativeDriver: true
}).start()
}
}, [active]);
const rotationX = transformX.interpolate({
inputRange: [0, 1],
outputRange: [2, Dimensions.get('screen').width / 4]
})
return (
<SafeAreaView style={{
alignItems: 'center',
backgroundColor:'transparent'
}}>
<View style={{
flexDirection: 'row',
position: 'relative',
height: 45,
width: 240,
borderRadius: 10,
backgroundColor: 'white',
marginHorizontal: 5
}}>
<Animated.View
style={{
position: 'absolute',
height: 45 - 2*2,
top: 2,
bottom: 2,
borderRadius: 10,
width: Dimensions
.get('screen').width / 3 - 3.5 ,
transform: [
{
translateX: rotationX
}
],
backgroundColor: '#d1cfcf',
}}
>
</Animated.View>
<TouchableOpacity style={{
flex: 1,
justifyContent: 'center',
alignItems: 'center'
}} onPress={() => {setActive(false); props.renderList() }}>
<Text>
List
</Text>
</TouchableOpacity>
<TouchableOpacity style={{
flex: 1,
justifyContent: 'center',
alignItems: 'center'
}} onPress={() => {setActive(true); props.renderMap() }}>
<Text>
Map
</Text>
</TouchableOpacity>
</View>
</SafeAreaView>
);
}
export default Slider
I update Slider Component in Home.js as below.
<Slider
renderMap={goToMap}
renderList={goToList}
active={route.params && route.params.componentToShow==='Screen2'}
/>
Check full working snack:
https://snack.expo.dev/#emmbyiringiro/283f93

Related

How to make Toggle Button with Animated.View in React Native?

I can only use react native in the project, I need to make a Toggle Component with AnimatedView. I tried with react native switcher but it won't be responsive for mobile and web at the same time.
Here is my code
export const ToggleButton = () => {
const [isEnabled, setIsEnabled] = useState(false);
const [text, setText] = useState('');
const toggleSwitch = () => {
if (isEnabled) {
setText('OFF');
} else {
setText('ON');
}
setIsEnabled(previousState => !previousState);
};
return (
<View style={styles.container}>
<View>
{isEnabled ? <Text style={styles.textOn}>On</Text> : <Text style={styles.textOff}>Off</Text>}
<Switch
trackColor={{ false: Colors.BlueLight, true: Colors.PurpleLight }}
thumbColor={isEnabled ? Colors.BlueLight : Colors.BlueLight}
ios_backgroundColor="#3E3E3E"
onValueChange={toggleSwitch}
value={isEnabled}
/>
</View>
</View>
);
};
Someone give me a recommendation how to do it?
Hye finally i made a custom switch, do check out :
Do check out this expo https://snack.expo.dev/#gaurav1995/gnarly-sandwich
Its completely built with react native, no external libraries etc
Do lemme know in case of any doubts :)
import React, { useState, useRef } from 'react';
import {
Text,
View,
StyleSheet,
Animated,
TouchableOpacity,
Easing
} from 'react-native';
export default function App() {
const positionButton = useRef(new Animated.Value(0)).current;
const [isOn, setIsOn] = useState(false);
const startAnimToOff = () => {
Animated.timing(positionButton,{
toValue:0,
duration:500,
easing:Easing.ease
}).start()
};
const startAnimToOn = () => {
Animated.timing(positionButton,{
toValue:1,
duration:500,
easing:Easing.ease
}).start()
};
const positionInterPol = positionButton.interpolate({inputRange:[0,1],outputRange:[0,30]})
const backgroundColorAnim = positionButton.interpolate({inputRange:[0,1],outputRange:["#767577","#81b0ff"]})
const initialOpacityOn = positionButton.interpolate({inputRange:[0,1],outputRange:[0,1]})
const initialOpacityOff = positionButton.interpolate({inputRange:[0,1],outputRange:[1,0]})
const onPress = () => {
if (isOn) {
startAnimToOff();
setIsOn(false);
} else {
startAnimToOn();
setIsOn(true);
}
};
return (
<View style={styles.container}>
<TouchableOpacity style={{height:30,width:60}} activeOpacity={0.9} onPress={onPress} >
<Animated.View style={[styles.mainStyes,{
backgroundColor:backgroundColorAnim
}]} >
<Animated.Text
style={[
styles.eahcStyles,
{
opacity: initialOpacityOn,
},
]}>
ON
</Animated.Text>
<Animated.Text
style={[
styles.eahcStylesOf,
{
opacity: initialOpacityOff,
},
]}>
OFF
</Animated.Text>
<Animated.View style={[styles.basicStyle,{
transform:[{
translateX:positionInterPol
}]
}]} />
</Animated.View>
</TouchableOpacity>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
backgroundColor: '#ecf0f1',
padding: 8,
},
basicStyle: {
height: 20,
width: 20,
borderRadius: 20,
backgroundColor: '#FFF',
marginTop: 5,
marginLeft: 5,
},
eahcStyles: {
fontSize: 14,
color: '#f5dd4b',
position: 'absolute',
top: 6,
left: 5,
},
eahcStylesOf: {
fontSize: 14,
color: '#f4f3f4',
position: 'absolute',
top: 6,
right: 5,
},
mainStyes: {
borderRadius: 30,
backgroundColor: '#81b0ff',
height: 30,
width: 60,
},
paragraph: {
margin: 24,
fontSize: 18,
fontWeight: 'bold',
textAlign: 'center',
},
});

React Native - updating UI when in a loop using functional component

I am developing a React Native app and I have a process whereby I want to put a progress center or progress bar in the UI as it runs.
I found this code in SO but I'm not sure how to implement in my functional component.
Could please give me an example of how to call ProgressBar from my func component and how to increase progress value.?
Thanks in advance.
const ProgressBar = (props)=>{
const [value] = useState(new Animated.Value(props.value))
useEffect(()=>{
Animated.timing(value, {
toValue: props.value,
duration: 100,
}).start()
}, [props.value])
const width = value.interpolate({
inputRange: [0, 100],
outputRange: ['0%', '100%'],
})
return (
<View style={{
width: '100%',
height: '100%',
flexDirection: 'row',
backgroundColor: 'white',
}}>
<Animated.View style={{
width: width,
height: '100%',
backgroundColor: 'green',
}}></Animated.View>
</View>
)
}
Check a fully working example with progress value change.
import * as React from "react";
import { useEffect, useState } from "react";
import { Text, View, StyleSheet, Animated } from "react-native";
import Slider from "#react-native-community/slider";
import Constants from "expo-constants";
const ProgressBar = (props) => {
const [value] = useState(new Animated.Value(props.value));
useEffect(() => {
Animated.timing(value, {
toValue: props.value,
duration: 100,
}).start();
}, [props.value]);
const width = value.interpolate({
inputRange: [0, 100],
outputRange: ["0%", "100%"],
});
return (
<View
style={{
width: "100%",
height: "100%",
flexDirection: "row",
backgroundColor: "white",
}}
>
<Animated.View
style={{
width: width,
height: "100%",
backgroundColor: "green",
}}
></Animated.View>
</View>
);
};
export default function App() {
const [progress, setProgress] = useState(10);
return (
<View style={styles.container}>
<View style={{ paddingVertical: 20 }}>
<Text> Change slider value to see progress bar follow </Text>{" "}
</View>
<View style={{ height: 20 }}>
{" "}
<ProgressBar value={progress} />{" "}
</View>
<View style={{ paddingVertical: 20 }}>
<Slider
minimumValue={0}
maximumValue={100}
onValueChange={setProgress}
/>
</View>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: "center",
paddingTop: Constants.statusBarHeight,
backgroundColor: "#ecf0f1",
padding: 8,
},
paragraph: {
margin: 24,
fontSize: 18,
fontWeight: "bold",
textAlign: "center",
},
});
Expo snack - https://snack.expo.dev/#emmbyiringiro/30efea
Use setter to set the value
const [progress, setProgress] = useState(props.value)
useEffect(()=>{
Animated.timing(value, {
toValue: progress,
duration: 100,
}).start()
}, [progress])
to update progress bar, call setProgress(newValue)

How animate Opacity in Flatlist with 2 columns

I try to make a little opacity animation when items touch the top of the flatlist.
The problem is that just one item rendered with the animation and not 2 Items at the same row.
I want that as soon as the top of these two Items touch the top of the Flatlistview, the opacity decrease until two others Items's top below. And so on.
I put my code here if anyone has some solution for that.
Thank you !
import React, { Component, Fragment } from 'react';
import { View, StyleSheet,Dimensions, Button, Pressable, TouchableOpacity, SafeAreaView, Text, KeyboardAvoidingView, Switch,Image,FlatList, Animated } from 'react-native';
import { createStackNavigator, createAppContainer } from 'react-navigation';
import Data_hobbys from '../../Data_hobbys';
import { TextInput,ProgressBar , Colors } from 'react-native-paper';
const DATA = Data_hobbys;
const numColumns = 2;
const { height } = Dimensions.get("screen");
const Item = ({ title, path, opacity }) => (
<Animated.View style = {[styles.item, {opacity: opacity}]}>
<Image style = {styles.imageStyle} source = {path}/>
<Text style={styles.title}>{title}</Text>
</Animated.View>
);
const Sports_selection = () => {
const scrollY = React.useRef(new Animated.Value(0)).current
const renderItem = ({ item, index}) => {
const opacityInputRange = [
-1,
0,
150 * index,
150 * (index + 1)
]
const opacity = scrollY.interpolate({
inputRange: opacityInputRange,
outputRange: [1 ,1 ,1 ,0]
})
return (<Item title={item.title} path = {item.path} opacity = {opacity} />
);
}
return(
<View style = {styles.main_container}>
<ProgressBar progress={0.4} style = {styles.progressBar} color = '#D1A552'/>
<Text style = {styles.introduction_text}>Do you some sport or watch it ?</Text>
<Text style = {styles.instruction_text}>Yes ? Great select it !</Text>
<Animated.FlatList
onScroll = {Animated.event(
[{ nativeEvent: {contentOffset: {y: scrollY}}}],
{useNativeDriver: true}
)}
//contentContainerStyle={{flexDirection : "row", flexWrap : "wrap"}}
columnWrapperStyle={{justifyContent: 'space-evenly'}}
data = {DATA}
renderItem = {renderItem}
keyExtractor = {(item, index) => item.id}
numColumns = {numColumns}
style={{
height: "60%",
flexGrow: 0
}}
//xscrollEventThrottle={16}
style = {styles.flatlistStyle}
/>
<TouchableOpacity style = {styles.buttonNext}>
<Text style = {styles.TextButton}>Next</Text>
</TouchableOpacity>
</View>
);
}
export default Sports_selection;
const styles = StyleSheet.create({
main_container: {
flex: 1,
backgroundColor: 'white',
},
introduction_text: {
fontSize : 30,
fontWeight : 'bold',
color : '#D1A552',
textAlign : 'left',
paddingLeft: 15,
marginTop: '30%',
},
instruction_text: {
fontSize : 20,
fontWeight : 'normal',
color : 'black',
textAlign : 'left',
paddingLeft: 15,
marginTop: '2%',
},
item: {
justifyContent: 'center',
alignContent:'center',
height : 150,
width : 150,
marginVertical: 5,
},
title: {
fontWeight : 'normal',
paddingTop: 12,
fontSize: 15,
textAlign:'center',
},
imageStyle:{
alignSelf: 'center',
width: 100,
height: 100,
},
flatlistStyle:{
marginTop:'6%',
height: "50%",
flexGrow: 0,
},
buttonNext:{
position: 'absolute',
alignSelf: 'center',
backgroundColor: "#D1A552",
borderRadius: 30,
width: "40%",
height: 45,
justifyContent: "center",
bottom: "10%",
alignItems: "center",
},
TextButton:{
fontWeight : 'bold',
color: 'white',
fontSize: 20,
textAlign:'center',
},
progressBar: {
top: 70,
width: "70%",
alignSelf: 'center',
},
})
you need to calculate the scrollY with high of item
const HIGHT_OF_ITEM=100
const HIGHT_OF_CONTAINER=HIGHT_OF_ITEM+20
const Sports_selection = () => {
const scrollY = React.useRef(new Animated.Value(0)).current;
const renderItem = ({ item, index }) => {
const input_cal=(value)=>{
return value + HIGHT_OF_CONTAINER*(Math.floor(index/numColumns))
}
const opacityInputRange = [ input_cal(0), input_cal(50), input_cal(80),input_cal(100) ];
const opacity = scrollY.interpolate({
inputRange: opacityInputRange,
outputRange: [1,0.5,0.3,0]
, extrapolate:'clamp'
});
return <Item title={item.title} path={item.path} opacity={opacity} />;
};
snack demo

react-native-pdf cannot set currentPage state to show up on display

i've done the react-native-pdf to show the slides of my pdf file. But, I want to set the current page state to show up on the display. And when the value is set, it'll refresh that screen and goes back to the first page automatically.
this is my code:
import React, { useState } from 'react';
import { StyleSheet, Dimensions, View, Text } from 'react-native';
import Pdf from 'react-native-pdf';
function Work() {
const [currentPage, setCurrentPage] = useState()
const ShowPdf = () => {
const source = { uri: 'http://samples.leanpub.com/thereactnativebook-sample.pdf', cache: true };
return (
<Pdf
source={source}
onLoadComplete={(numberOfPages, filePath) => {
console.log(`number of pages: ${numberOfPages}`);
}}
onPageChanged={(page, numberOfPages) => {
console.log(`current page: ${page}`);
setCurrentPage(page) //set the cuurentPage
}}
onError={(error) => {
console.log(error);
}}
onPressLink={(uri) => {
console.log(`Link presse: ${uri}`)
}}
style={styles.pdf} />
)
}
return (
<View style={styles.container}>
<ShowPdf />
<View style={styles.pageNumber}>
<Text>{currentPage}</Text>
</View>
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'flex-start',
alignItems: 'center',
//marginTop: 25,
//backgroundColor: 'red',
backfaceVisibility: 'hidden'
},
pdf: {
flex: 1,
width: Dimensions.get('window').width,
height: Dimensions.get('window').height,
},
pageNumber: {
position: 'absolute',
left: 20,
top: 20,
backgroundColor: 'rgba(173, 173, 173, 0.5)',
padding: 13,
borderRadius: 6,
}
});
export default Work;
my emulator display:
image
Anyway I can fix this?
Instead of use this
<View style={styles.container}>
<ShowPdf />
<View style={styles.pageNumber}>
<Text>{currentPage}</Text>
</View>
</View>
please use it
<View style={styles.container}>
<Pdf
source={{ uri: path }}
onLoadComplete={(numberOfPages, filePath) => {
console.log(`number of pages: ${numberOfPages}`);
}}
onPageChanged={(page, numberOfPages) => {
console.log(`current page: ${page}`);
setCurrentPage(page) //set the cuurentPage
}}
onError={(error) => {
console.log(error);
}}
onPressLink={(uri) => {
console.log(`Link presse: ${uri}`)
}}
style={styles.pdf} />
<View style={styles.pageNumber}>
<Text>{currentPage}</Text>
</View>
</View>
<ShowPdf/> is your custom react component it will be re-rendered for every page change. So, that is why you faced this problem

react native: rendering cards that flip on user tap

I'm working on a React Native code that renders “cards” on an array using a map function. Each card is wrapped in a touchableOpacity component, so that when a user taps the card, it would flip. Currently the issue is that, if a user taps to flip one card, all neighboring cards flip as well. I would like to have the flip functionality for each card be independent. When a card is flipped it should not trigger the flipping of neighboring cards as well. Thanks in advance to reading this.
class SavedBooks extends Component {
componentWillMount() {
this.animatedValue = new Animated.Value(0);
this.value = 0;
this.animatedValue.addListener(({ value }) => { this.value = value })
}
frontCardStyle() {
this.frontInterpolate = this.animatedValue.interpolate({
inputRange: [0, 180],
outputRange: ['0deg', '180deg']
})
const frontAnimatedStyle = {
transform: [ { rotateY: this.frontInterpolate }]
}
return frontAnimatedStyle
}
backCardStyle() {
this.backInterpolate = this.animatedValue.interpolate({
inputRange: [0, 180],
outputRange: ['180deg', '360deg']
})
const backAnimatedStyle = { transform: [{ rotateY: this.backInterpolate }] }
return backAnimatedStyle
}
flipCard() {
if (this.value >= 90) {
Animated.spring(this.animatedValue, {
toValue: 0,
friction: 8,
tension: 10
}).start();
} else if (this.value < 90) {
Animated.spring(this.animatedValue, {
toValue: 180,
friction: 8,
tension: 10
}).start();
}
}
renderElemets(color) {
const { savedBooks } = this.props.book
return savedBooks.map((book, index) => {
return (
<View
key={index}
style={{ alignItems: 'center' }}>
<TouchableOpacity onPress={() => this.flipCard()} >
<Text
style={{ fontFamily: 'Helvetica',
fontSize: 25,
padding: 15 }}>
{book.title}
</Text>
<Animated.View>
<Animated.Image
style={[this.frontCardStyle(), styles.cardStyle]}
source={{ uri: book.image }}
/>
<Animated.View style={[this.backCardStyle(), styles.cardStyle, styles.flipCardBack]}>
<Text>{book.description}</Text>
</Animated.View>
</Animated.View>
</TouchableOpacity>
</View>
)
});
}
render() {
return (
<ScrollView>
{this.renderElemets(color)}
</ScrollView>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#FFF',
},
imageStyle: {
position: 'absolute',
top: 0,
left: 0,
bottom: 0,
right: 0,
},
cardStyle: {
height: 400,
width: 250,
backfaceVisibility: 'hidden',
},
flipCardBack: {
position: "absolute",
top: 0,
},
});
Because all the cards share the same style. You should create a card component so each card can have their own style and the press event won't affect other cards.
I had this exact problem and solved it by adding something like an identifier or tag to the clicked item in the array. The idea is quite similar to adding a class 'active' to the item clicked in the array, that way your flipCard() function will only be run on the item which has been labelled active. In short, only add the frontCardStyle() or backCardStyle() to the item that has been clicked and labelled 'active'. That way only the active item will flip.
I followed this example and came up with the solution below;
constructor(props) {
super(props);
this.state = {
activeItem: {},
}
this.toggleActiveItem = this.toggleActiveItem.bind(this);
}
toggleActiveItem(index) {
this.setState({
activeItem: {
[index]: true
}
});
this.flipCard();
}
renderElemets(color) {
const { savedBooks } = this.props.book
return savedBooks.map((book, index) => {
return (
<View
key={index}
style={{ alignItems: 'center' }}>
<TouchableOpacity onPress={() => this.toggleActiveItem(index)} >
<Text
style={{ fontFamily: 'Helvetica',
fontSize: 25,
padding: 15 }}>
{book.title}
</Text>
<Animated.View>
<Animated.Image
style={[this.state.activeItem[index] && this.frontCardStyle(), styles.cardStyle]}
source={{ uri: book.image }}
/>
<Animated.View style={[this.state.activeItem[index] && this.backCardStyle(), styles.cardStyle, styles.flipCardBack]}>
<Text>{book.description}</Text>
</Animated.View>
</Animated.View>
</TouchableOpacity>
</View>
)
});
}

Resources