React Native - Access properties of exported functional component - javascript

Here is my code for a custom functional component (./Animations/ModalViewMoveUp.js)
import React, { useRef, useEffect } from 'react';
import { Animated, Easing, Text, View } from 'react-native';
import { ExtendedExceptionData } from 'react-native/Libraries/LogBox/LogBox';
export const ModalViewMoveUp = (props) => {
const moveAnim = useRef(new Animated.Value(900)).current
const animIn = Animated.timing(
moveAnim,
{
toValue: 50,
duration: 1700,
easing: Easing.elastic(),
useNativeDriver:false
}
)
const animOut = Animated.timing(
moveAnim,
{
toValue: 1000,
duration: 1700,
easing: Easing.elastic(),
useNativeDriver:false
}
)
React.useEffect(() => {
animIn.start();
}, [moveAnim])
return (
<Animated.View
style={{
...props.style,
marginTop: moveAnim,
}}
>
{props.children}
</Animated.View>
);
}
And in ./App.js
import {ModalIconViewMoveDown} from './Animations/ModalIconViewMoveDown'
...
<ModalViewMoveUp >
{/* other elements */}
<TouchableOpacity onPress={()=>???}><Text>CANCEL</Text></TouchableOpacity>
</ModalViewMoveUp >
I basically want a way to start the exit animation animOut.start() on when the cancel button is pressed, but i cant even get a ref of the ModalViewMoveUp element in the TouchableOpacity , let alone call a function from it.
Even something like this would work for me
<ModalViewMoveUp shouldStop={this.state.shouldStop}>
{/* other elements */}
<TouchableOpacity onPress={()=>setState{{shouldStop:true}}}><Text>CANCEL</Text></TouchableOpacity>
</ModalViewMoveUp >
And then in /ModalViewMoveUp.js
React.useEffect(() => {
if(shouldStop){
animOut.start();
}
//animIn.start() runs on render()
}, [moveAnim])
But i know i cant just set a state in an exported component.
Do i have to convert my functional component to a class?
Any code examples would be appreciated!

What i ended up doing: pass a state bool when calling the component
<ModalViewMoveUp shouldOpen ={this.state.shouldOpen } style ={styles.modalViewUp}>
{/* other elements */}
<TouchableOpacity onPress={()=>setState{{shouldOpen:true }}}><Text>CANCEL</Text></TouchableOpacity>
</ModalViewMoveUp >
and in /ModalViewMoveUp.js
return(
<View>
{props.shouldOpen == true &&
<Animated.View
style={{
...props.style,
marginTop: moveAnim,
}}
>
{props.children}
</Animated.View>
}
{props.shouldOpen == false &&
<Animated.View
style={{
...props.style,
marginTop: moveAnim,
}}
>
{animOut.start()}
{props.children}
</Animated.View>
}
</View>
);
)
It works exactly as i want it to. If anyone has any contraindications feel free to share !

Related

Why my react-native-shadow-2 is not working

Why my react-native-shadow-2 is not working ? i have installed npm i react-native-shadow-2 and react-native-svg but still its not working tell how to fix it i have also restarted my project i am using android i have also tried to do npx react-native link but that also did not work you can see whats the problem the shadow i have marked is wrong it should be like this
How its showing currently
my code
import React, { useRef } from 'react'
import {
View,
Text,
Image,
Animated,
FlatList,
} from 'react-native';
import {
Home,
Profile,
Search
} from '../../screens';
import { COLORS, FONTS, SIZES, constants } from '../../constants';
import { Shadow } from 'react-native-shadow-2';
const bottom_tabs = constants.bottom_tabs.map((bottom_tabs) => ({
...bottom_tabs,
ref: React.createRef()
}))
const MainLayout = () => {
const flatListRef = React.useRef()
const scrollX = React.useRef(new Animated.Value(0)).current;
function renderContent() {
return (
<View style={{
flex: 1,
}}
>
<Animated.FlatList
ref={flatListRef}
horizontal
pagingEnabled
snapToAlignment="center"
snapToInterval={SIZES.width}
decelerationRate="fast"
showsHorizontalScrollIndicator={false}
data={constants.bottom_tabs}
keyExtractor={item => `Main-${item.id}`}
onScroll={
Animated.event([
{ nativeEvent: { contentOffset: { x: scrollX } } }
], {
useNativeDriver: false
})
}
renderItem={({ item, index }) => {
return (
<View
style={{
height: SIZES.height,
width: SIZES.width
}}
>
{item.label == constants.screens.home && <Home />}
{item.label == constants.screens.search && <Search />}
{item.label == constants.screens.profile && <Profile />}
</View>
)
}}
/>
</View>
)
}
function renderBottonTab() {
return (
<View
style={{
marginBottom: 20,
paddingHorizontal: SIZES.padding,
paddingVertical: SIZES.radius
}}
>
<Shadow size={[SIZES.width - (SIZES.padding * 2), 85]}>
<View
style={{
flex: 1,
borderRadius: SIZES.radius,
backgroundColor: COLORS.primary3
}}
>
</View>
</Shadow>
</View>
)
}
return (
<View
style={{
flex: 1,
backgroundColor: COLORS.white,
}}
>
{/* Content */}
{renderContent()}
{/* Bottom Tab */}
{renderBottonTab()}
</View>
)
}
export default MainLayout;

When animating and changing state, the animation disappears(react native)

I am using react native with expo cli and I have a component:
import React, {useEffect, useState} from 'react'
import {View, TextInput, Text, TouchableOpacity, Animated, Easing} from 'react-native';
import s from './Login_style'
import {connect} from "react-redux";
const LoginInner = (props) => {
const [mode, setMode] = useState(true)
const btnAnim = new Animated.Value(0)
const setModeAnim = (is) => {
if (is) {
Animated.timing(btnAnim, {
toValue: 1,
duration: 300,
easing: Easing.out(Easing.exp),
useNativeDriver: false,
}).start()
// setMode(false)
} else {
Animated.timing(btnAnim, {
toValue: 0,
duration: 300,
easing: Easing.out(Easing.exp),
useNativeDriver: false
}).start()
// setMode(true)
}
}
const size1 = btnAnim.interpolate({
inputRange: [0, 1],
outputRange: ['30%', '60%']
})
const size2 = btnAnim.interpolate({
inputRange: [0, 1],
outputRange: ['60%', '30%']
})
return (
<View style={s.page}>
<View style={s.inputs}>
<Text style={[s.text, s.title]}>{mode ? 'Вход' : 'Регистрация'}</Text>
<View style={s.textInput}>
<View>
<Text style={[s.text, s.placeholder]}>Логин</Text>
</View>
<TextInput style={s.input}/>
</View>
<View style={s.textInput}>
<View>
<Text style={[s.text, s.placeholder]}>Пароль</Text>
</View>
<TextInput style={s.input}/>
</View>
<View style={s.actions}>
<Animated.View style={[s.btn, s.loginBtn, {width: size2}]}>
<TouchableOpacity style={s.touchableBtn} onPress={() => {
setModeAnim(false)
}}>
<Text style={s.loginBtn_text}>{mode ? 'Вход' : '<--'}</Text>
</TouchableOpacity>
</Animated.View>
<Animated.View style={[s.btn, s.regBtn, {width: size1}]}>
<TouchableOpacity style={s.touchableBtn} onPress={() => {
setModeAnim(true)
}}>
<Text style={s.loginBtn_text}>{mode ? '-->' : 'Регистрация'}</Text>
</TouchableOpacity>
</Animated.View>
</View>
</View>
</View>
)
}
const Login = connect((state) => {
return {}
}, {})(LoginInner)
export default Login
Here I am interested in this function
const setModeAnim = (is) => {
if (is) {
Animated.timing(btnAnim, {
toValue: 1,
duration: 300,
easing: Easing.out(Easing.exp),
useNativeDriver: false,
}).start()
// setMode(false)
} else {
Animated.timing(btnAnim, {
toValue: 0,
duration: 300,
easing: Easing.out(Easing.exp),
useNativeDriver: false
}).start()
// setMode(true)
}
}
I run the animation here when the function is executed (ignore the commented out line for now). The animation really works.
VIDEO DEMONSTRATION: https://youtu.be/fiGy0gNej68
But if I change the state of the component in this function after starting the animation:
setMode(false) or setMode(true) animation does not start, even the buttons do not change their size
VIDEO DEMONSTRATION: https://youtu.be/deLZEKVnaBY
Tell me how to solve this
Thanks
To add to #LIMPIX64's answer:
using const btnAnim = React.useRef(new Animated.Value(0)).current solves the problem because since Animated.Value is an instance of a class, you need to wrap it in a useRef call so that it only gets created once when the component renders for the first time (which is what useRef accomplishes). Otherwise, the class could be reinstantiated on a state change and cause the issue you're experiencing
Please do const btnAnim = React.useRef(new Animated.Value(0)).current – Harrison

React Native: Passing useState() data to unrelated screens

Explanation: I am creating a fitness app, my fitness app has a component called WorkoutTimer that connects to the workout screen, and that screen is accessed via the HomeScreen. Inside the WorkoutTimer, I have an exerciseCount useState() that counts every time the timer does a complete loop (onto the next exercise). I have a different screen called StatsScreen which is accessed via the HomeScreen tab that I plan to display (and save) the number of exercises completed.
What I've done: I have quite literally spent all day researching around this, but it seems a bit harder with unrelated screens. I saw I might have to use useContext() but it seemed super difficult. I am fairly new to react native so I am trying my best haha! I have attached the code for each screen I think is needed, and attached a screenshot of my homeScreen tab so you can get a feel of how my application works.
WorkoutTimer.js
import React, { useState, useEffect, useRef } from "react";
import {
StyleSheet,
Text,
View,
TouchableOpacity,
Button,
Animated,
Image,
SafeAreaView,
} from "react-native";
import { CountdownCircleTimer } from "react-native-countdown-circle-timer";
import { Colors } from "../colors/Colors";
export default function WorkoutTimer() {
const [count, setCount] = useState(1);
const [exerciseCount, setExerciseCount] = useState(0);
const [workoutCount, setWorkoutCount] = useState(0);
const exercise = new Array(21);
exercise[1] = require("../assets/FR1.png");
exercise[2] = require("../assets/FR2.png");
exercise[3] = require("../assets/FR3.png");
exercise[4] = require("../assets/FR4.png");
exercise[5] = require("../assets/FR5.png");
exercise[6] = require("../assets/FR6.png");
exercise[7] = require("../assets/FR7.png");
exercise[8] = require("../assets/FR8.png");
exercise[9] = require("../assets/S1.png");
exercise[10] = require("../assets/S2.png");
exercise[11] = require("../assets/S3.png");
exercise[12] = require("../assets/S4.png");
exercise[13] = require("../assets/S5.png");
exercise[14] = require("../assets/S6.png");
exercise[15] = require("../assets/S7.png");
exercise[16] = require("../assets/S8.png");
exercise[17] = require("../assets/S9.png");
exercise[18] = require("../assets/S10.png");
exercise[19] = require("../assets/S11.png");
exercise[20] = require("../assets/S12.png");
exercise[21] = require("../assets/S13.png");
return (
<View style={styles.container}>
<View style={styles.timerCont}>
<CountdownCircleTimer
isPlaying
duration={45}
size={240}
colors={"#7B4FFF"}
onComplete={() => {
setCount((prevState) => prevState + 1);
setExerciseCount((prevState) => prevState + 1);
if (count == 21) {
return [false, 0];
}
return [(true, 1000)]; // repeat animation for one second
}}
>
{({ remainingTime, animatedColor }) => (
<View>
<Image
source={exercise[count]}
style={{
width: 150,
height: 150,
}}
/>
<View style={styles.timeOutside}>
<Animated.Text
style={{
color: animatedColor,
fontSize: 18,
position: "absolute",
marginTop: 67,
marginLeft: 35,
}}
>
{remainingTime}
</Animated.Text>
<Text style={styles.value}>seconds</Text>
</View>
</View>
)}
</CountdownCircleTimer>
</View>
</View>
);
}
const styles = StyleSheet.create({})
WorkoutScreen.js
import React, { useState } from "react";
import { StyleSheet, Text, View } from "react-native";
import WorkoutTimer from "../components/WorkoutTimer";
export default function WorkoutScreen() {
return (
<View style={styles.container}>
<WorkoutTimer />
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#fff",
alignItems: "center",
justifyContent: "center",
},
});
HomeScreen.js
import React from "react";
import { StyleSheet, Text, View, SafeAreaView, Button } from "react-native";
import { TouchableOpacity } from "react-native-gesture-handler";
import { AntDesign } from "#expo/vector-icons";
import { Colors } from "../colors/Colors";
export default function HomeScreen({ navigation }) {
return (
<SafeAreaView style={styles.container}>
<Text style={styles.pageRef}>SUMMARY</Text>
<Text style={styles.heading}>STRETCH & ROLL</Text>
<View style={styles.content}>
<TouchableOpacity
style={styles.timerDefault}
onPress={() => navigation.navigate("WorkoutScreen")}
>
<Button title="START WORKOUT" color={Colors.primary} />
</TouchableOpacity>
<TouchableOpacity
style={styles.statContainer}
onPress={() => navigation.navigate("StatsScreen")}
>
<AntDesign name="barschart" size={18} color={Colors.primary} />
<Text style={{ color: Colors.primary }}>Statistics</Text>
<AntDesign name="book" size={18} color={Colors.primary} />
</TouchableOpacity>
</View>
</SafeAreaView>
);
}
const styles = StyleSheet.create({})
StatsScreen.js
import React from "react";
import { StyleSheet, Text, View } from "react-native";
import { exerciseCount, workoutCount } from "../components/WorkoutTimer";
export default function StatsScreen() {
return (
<View style={styles.container}>
<Text display={exerciseCount} style={styles.exerciseText}>
{exerciseCount}
</Text>
<Text display={workoutCount} style={styles.workoutText}>
{workoutCount}
</Text>
</View>
);
}
const styles = StyleSheet.create({});
Home Screen Image
As far as I can tell, you're almost there! You're trying to get your 2 state
variables from the WorkoutTimer like this:
import { exerciseCount, workoutCount } from "../components/WorkoutTimer";
Unfortunatly this won't work :( . These two variables change throughout your
App's life-time and that kinda makes them "special".
In React, these kinds of variables need to be declared in a parent component
and passed along to all children, which are interested in them.
So in your current Setup you have a parent child relationship like:
HomeScreen -> WorkoutScreen -> WorkoutTimer.
If you move the variables to HomeScreen (HomeScreen.js)
export default function HomeScreen({ navigation }) {
const [exerciseCount, setExerciseCount] = useState(0);
const [workoutCount, setWorkoutCount] = useState(0);
you can then pass them along to WorkoutScreen or StatsScreen with something
like:
navigation.navigate("WorkoutScreen", { exerciseCount })
navigation.navigate("StatsScreen", { exerciseCount })
You'll probably have to read up on react-navigation's documentation for .navigate I'm not sure I remember this correctly.
In order to read the variable you can then:
export default function WorkoutScreen({ navigation }) {
const exerciseCount = navigation.getParam(exerciseCount);
return (
<View style={styles.container}>
<WorkoutTimer exerciseCount={exerciseCount} />
</View>
);
}
and finally show it in the WorkoutTimer:
export default function WorkoutTimer({ exerciseCount }) {
Of course that's just part of the solution, since you'll also have to pass
along a way to update your variables (setExerciseCount and setWorkoutCount).
I encourage you to read through the links I posted and try to get this to work.
After you've accumulated a few of these stateful variables, you might also want to look at Redux, but this is a bit much for now.
Your app looks cool, keep at it!
I ended up solving this problem with useContext if anyone is curious, it was hard to solve initially. But once I got my head around it, it wasn't too difficult to understand.
I created another file called exerciseContext with this code:
import React, { useState, createContext } from "react";
const ExerciseContext = createContext([{}, () => {}]);
const ExerciseProvider = (props) => {
const [state, setState] = useState(0);
//{ exerciseCount: 0, workoutCount: 0 }
return (
<ExerciseContext.Provider value={[state, setState]}>
{props.children}
</ExerciseContext.Provider>
);
};
export { ExerciseContext, ExerciseProvider };
and in App.js I used ExerciseProvider which allowed me to pass the data over the screens.
if (fontsLoaded) {
return (
<ExerciseProvider>
<NavigationContainer>
<MyTabs />
</NavigationContainer>
</ExerciseProvider>
);
} else {
return (
<AppLoading startAsync={getFonts} onFinish={() => setFontsLoaded(true)} />
);
}
}
I could call it with:
import { ExerciseContext } from "../components/ExerciseContext";
and
const [exerciseCount, setExerciseCount] = useContext(ExerciseContext);
This meant I could change the state too! Boom, solved! If anyone needs an explanation, let me know!
I think you have to use Mobx or Redux for state management. That will be more productive for you instead built-in state.

Use fat arrow function to change React.Component in React-Native

is it possible to render a React.Component over other React.Component using just fat arrow function, using state seems unnecessary in my case as there is no need to close the opened Component. I am trying to achieve the simplest to render a React.Component over other React.Component.
I am trying to do it like this:
<Button onPress={() => { return (<ShowOtherReactComponent/>); }} >Show OtherComponent</Button>
this is calling the <ShowOtherReactComponent/> I know that because I called an alert function from constructor but! nothing is rendering. why is that? how can I do this?
PS: this approach may be wrong, but still wanna see how it can be done. for science.
You shouldn't return jsx from your handlers. Usually to show and or toggle components conditional rendering is the way to go.
Instead of returning <ShowOtherReactComponent/> from onPress you conditionally render the component based on a boolean binded to the local state and change the state instead.
const Component = () =>{
const [show, setShow] = useState(false)
const onPress = () => setShow(true)
return(
<>
<button onPress={onPress}> Show </button>
{ show && <ShowOtherReactComponent/> }
</>
)
}
I've made an example to show what you could potentially do if you wanted a button to add components to display:
import React from 'react';
import autoBind from 'react-autobind';
export default class ButtonTest extends React.Component {
constructor(props) {
super(props);
this.state = {
extraComponents : []
};
autoBind(this);
}
addComponent() {
const newComponent = (<p>I'm a new component</p>);
this.setState({extraComponents: [...this.state.extraComponents, newComponent]})
}
render() {
return (
<div>
<button onClick={this.addComponent}>add component</button>
{this.state.extraComponent}
</div>
)
}
}
I've checked it and it works.
import React, { useState } from 'react'
import { SafeAreaView, View, Text, Button, Dimensions } from 'react-native'
const App = () => {
const [visibilityOfOtherView, setvisibilityOfOtherView] = useState(false)
const { height, width } = Dimensions.get('window')
const SCREEN_HEIGHT = Math.round(height)
const SCREEN_WIDTH = Math.round(width)
return (
<SafeAreaView style={{ height: SCREEN_HEIGHT, width: SCREEN_WIDTH, }}>
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center', backgroundColor: 'red' }}>
<Text style={{ marginBottom: 20 }}>
First Components
</Text>
<Button
title='Toggle Components View'
onPress={() => setvisibilityOfOtherView(!visibilityOfOtherView)}
/>
</View>
{
visibilityOfOtherView ?
<View style={{ height: SCREEN_HEIGHT, width: SCREEN_WIDTH, alignItems: 'center', justifyContent: 'center', backgroundColor: 'green' }}>
<Text style={{ marginBottom: 20 }}>
Secound Components
</Text>
<Button
title='Toggle Components View'
onPress={() => setvisibilityOfOtherView(!visibilityOfOtherView)}
/>
</View>
: null
}
</SafeAreaView>
)
}
export default App

React Native tasks not rendered until a refresh is done

I have a React Native component that is intended to render tasks in tinder-like card stack. It gets the tasks async from a Firebase backend and renders it into a card deck called Swiper. It also has three buttons, previous next and refresh, all of which get another task and renders the card. It seems to work but only after refresh/next/previous is called, until then the card stack is empty.
However, the console.log() in ComponentDidMount() has the same task data as getMore() does. Also on initial render there is a little pagination that shows there is one task loaded, just not the actual card.
Why doesn't the card show on initial load?
import React, { PureComponent, Component } from 'react';
import FitImage from 'react-native-fit-image';
import {View, H6, Text, StyleSheet,TouchableOpacity, Image} from "react-native";
import {inject, observer} from "mobx-react/native";
import moment from "moment";
import {Task as ITask} from "../Model";
import {Avatar, Styles,Task, Firebase, Circle, BaseContainer} from "../components";
import {H3,Content, Card, CardItem, Thumbnail, Button, Icon, Left, Body, Right, Tab, Tabs, TabHeading, H1, H2} from "native-base";
import variables from "../../native-base-theme/variables/commonColor";
import Swiper from 'react-native-swiper-animated';
export default class Lists extends Component {
render(): React$Element<*> {
return <BaseContainer title="Feed" navigation={this.props.navigation}>
<Feed />
</BaseContainer>;
}
}
export class Feed extends PureComponent {
swiper = null;
prev = () => {
this.swiper.forceLeftSwipe();
this.getMore();
}
next = () => {
this.swiper.forceRightSwipe();
this.getMore();
}
constructor(props) {
super(props);
this.state = { taskList: undefined } ;
}
getMore() {
Firebase.taskRef.limitToLast(1).on("value",snapshot => {
let tasks= _.map(snapshot.val(), task => task)
.filter(task => {
;
return !task.done;
});
if (tasks.length > 0) {
tasks = tasks.map((task, key) => (
<Task {...{task, key}} />
));
}
else tasks= [];
this.setState({ taskList: tasks });
console.log(this.state.taskList);
});
}
componentDidMount() {
Firebase.taskRef.limitToLast(1).on("value",snapshot => {
let tasks= _.map(snapshot.val(), task => task);
if (tasks.length > 0) {
tasks = tasks.map((task, key) => (
<Task {...{task, key}} />
);
}
else tasks= [];
this.setState({ taskList: tasks })
console.log(this.state.taskList);
});
}
render() {
return (
<View style={{ flex: 1 }}>
<Swiper
ref={(swiper) => {
this.swiper = swiper;
}}
showPagination={true}
paginationStyle={{ container: { backgroundColor: 'transparent' } }}
paginationLeft={''}
paginationRight={''}
swiper={false}
>
{ this.state.taskList }
</Swiper>
<View style={styles.buttonContainer}>
<Button danger style={{alignSelf: 'center', height: 65, borderRadius: 100,width:65, marginRight: 20}} onPress={this.prev} >
<Icon style={{fontSize: 30}} active name='thumbs-down' />
</Button>
<Button style={{backgroundColor: '#d8d8d8', alignSelf:'center', borderRadius: 100, width: 80, alignItems:'center', justifyContent:'center', height: 80}} onPress={this.next} >
<Icon style={{fontSize: 50}} active name='md-sync' />
</Button>
<Button success style={{alignSelf: 'center', height: 65, borderRadius: 100,width:65, marginLeft: 20}} onPress={this.next} >
<Icon style={{fontSize: 30}} active name='thumbs-up' />
</Button>
</View>
</View>
);
}
}

Categories