Passing State to another Component in React Native - react-native

React Native Newbie here.
I have an (in my opinion) common use case here. I work with React Navigation and have 4 different Tabs. In the first Tab I have a FlatList from which I want to choose Favourites. These Favourites should be then listed in the other Tab. Nothing more so far.
The Problem I encounter is that I'm not figuring out how I can transmit the favourites variable declared in my state of the first tab, to the other Tab. Maybe the approach is completely wrong too..
First Tab/Screen:
import React, {Component} from 'react';
import { FlatList, Text, View, ScrollView, Image, TouchableHighlight} from 'react-native';
import Icon from 'react-native-vector-icons/Ionicons'
export default class HomeScreen extends Component {
state = {
data: [],
favourites: []
};
//Function called on the click of the Heart Button, adding the List Element to the State
addFav = item => {
this.setState((prevState) => ({'favourites': prevState.favourites+item.title+' '}))
alert(item.title)
}
componentWillMount() {
this.fetchData();
}
//Fetching the data from the API
fetchData = async () => {
const response = await fetch("http://192.168.1.19:8080/v1/api/event");
const json = await response.json();
this.setState({ data: json});
};
render() {
return <FlatList
ItemSeparatorComponent={() =>
<View
style={{ height: 1, width: '100%', backgroundColor: 'lightgray' }}
/>
}
data={this.state.data}
keyExtractor={(item => item.title)}
renderItem={({ item }) =>
<ScrollView>
<Image style={{alignSelf:'stretch', width:undefined, height:undefined, flex : 1, borderRadius:10}} source = {require('../img/landscape.jpeg')}/>
<TouchableHighlight onPress={()=>this.openEvent(item)}>
<View style={{flex: 1, flexDirection:'row', padding: 5}}>
<View style={{flex: 1}}>
<Text style={{fontSize:15, textAlign:'center', padding: 2}}>{this.timeConverterMonth(item.time)}</Text>
<Text style={{fontSize:15, textAlign:'center', padding: 2}}>{this.timeConverterDay(item.time)}</Text>
</View>
<View style={{flex: 4}}>
<Text style={{fontSize:15, padding: 2}}>{item.title}</Text>
<Text style={{fontSize:10, padding: 2}}>{item.locShort}</Text>
</View>
//That's where the function is called
<TouchableHighlight
style={{flex: 2}}
onPress={() => this.addFav(item)}
>
<Icon name="ios-heart-empty" size={24} style={{alignSelf: 'center', padding: 10}}/>
</TouchableHighlight>
</View>
</TouchableHighlight>
//just checking the state
<Text>{this.state.favourites}</Text>
</ScrollView>}
/>;
}
}
Second Tab/Screen:
import React, {Component} from 'react';
import {Text} from 'react-native';
export default class FavouritesScreen extends Component {
constructor(props){
super(props)
}
render(){
//Here I want to display my favourites Array from the HomeScreen State
return <Text>{this.props.favourites}</Text>;
}
}
I am actually not wondering why it's not functioning, I just tried the props method by reading all the other articles but the Screens are not in Parent/Child relation.
So what I want to do would be in the Second Tab something like
HomeScreen.state.favourites
Thanks.

Your case is a very common one. One I faced was passing a 'shared state' between the application.
The components have a local state, which you can pass to child components via props (which you have mentioned).
The problem arises when you want to access that state in another component. The solution here is having a global state.
You may want to consider Redux for your application.
https://redux.js.org/introduction/getting-started
From the redux website:
Redux is a predictable state container for JavaScript apps.
It helps you write applications that behave consistently, run in
different environments (client, server, and native), and are easy to
test. On top of that, it provides a great developer experience, such
as live code editing combined with a time traveling debugger.
Essentially, you'll be getting a global state which can be accessed by all your application's components. This allows you to update states within one component and access them in another.
I will warn you, it's not the easiest thing to learn. When you first look at it - it's a bit daunting. But, as your application grows in complexity and you add more state - you'll be glad you used it.
The good news is, Redux is very well documented with React and React Native - so you should find lots of tutorials on how to integrate it into your current application.

Your usecase of having "globally" accessed state is where state management libraries come in. One good example is the libary Redux - in this case you could store the favourites under a piece of state called "HomeScreen" and map it and use it in any screen in the rest of the app.
Here is a good article about getting started with redux: https://blog.cloudboost.io/getting-started-with-react-native-and-redux-6cd4addeb29

Related

How to access params passed to a class based component in react navigation 5.0?

Using react-navigation you can pass parameters like this:
this.props.navigation.navigate(screen, {param: value});
and in previous versions you could access the parameters like this:
props.navigation.state.params
In the new version of react-navigation (5.0) they use functional components, and it appears you cannot access the params as you did before. Instead, you write components and access params like this:
function HomeScreen({ navigation, route }) {
React.useEffect(() => {
if (route.params?.post) {
// Post updated, do something with `route.params.post`
// For example, send the post to the server
}
}, [route.params?.post]);
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Button
title="Create post"
onPress={() => navigation.navigate('CreatePost')}
/>
<Text style={{ margin: 10 }}>Post: {route.params?.post}</Text>
</View>
);
}
How can I access the parameters passed to a component, without having to change all of the components in a project from class-based to functional-based components?
You just have change the code you used to access params from
props.navigation.state.params
to
props.route.params
All will be good no need to change class-based to functional-based components.
But i will suggest you start migrating at your own ease so you code will be as per the latest syntax.
You can see an example of this with the snippet here on this Expo snack
here on this Expo snack

React Navigation custom navigator transitions

I'm looking to create a Stack Navigator that can handle animating specific elements between 2 screens. Fluid Transitions looked like a library I could use, but it doesn't support react-navigation 5.X. If there's a package that has this functionality for react-navigation v5, that would be great.
However, if there is no current package for v5, I'd like to extend the StackNavigator to handle this kind of functionality. I've been able to remove the default animations for the StackNavigator with something similar to the following (where transition is a bool taken in the options prop for the Stack.Screen:
const CustomTransitionStackNavigator = ({
initialRouteName,
children,
screenOptions,
...rest
}) => {
if (descriptors[state.routes[state.index].key].options.transition) {
return (
<View style={{ flex: 1 }}>
{descriptors[state.routes[state.index].key].render()}
</View>
);
}
return (
<StackView
{...rest}
descriptors={descriptors}
navigation={navigation}
state={state}
/>
);
};
I'd like to be able to use a Context (or some other method) of passing the transition progress to the scene's descendants in order to handle the animations. Is there some way to get the transition progress in v5? Or would this CustomTransitionStackNavigator need to manage that state? Thanks!
You can use CardAnimationContext or useCardAnimation (which is just a convenience wrapper for the first one) to get transition progress in a stack navigator.
For example:
import { useCardAnimation } from '#react-navigation/stack';
import React from 'react';
import { Animated } from 'react-native';
export const SomeScreen = () => {
const { current } = useCardAnimation();
return (
<Animated.View
style={{
width: 200,
height: 200,
backgroundColor: 'red',
transform: [{ scale: current.progress }],
}}
/>
);
};
This feature seems to be undocumented at the moment, but you can check TypeScript definitions to get some more information.

How to send data to another component in react-native

I want to pass data to another component but not like this.
return (
<View style = {{flex:1}}>
---> <LoginForm profile = {this.state.values}/> // I dont want to send like this
<Animated.View style={{ ...this.props.style, opacity: fadeAnim }} >
{this.props.children}
</Animated.View>
</View>
);
because if I do like this, this component includes LoginForm as well. and also I dont want to send with navigate. Because I dont want to open that component on screen. when I work in this screen I just want to send values to another component
You need to pass a function to mutate the state from LoginForm to this child component.
On LoginForm,
<View>
...
<ChildComponent changeProfile={(profile) => {
this.setState({
profile: profile
})
}}/>
</View>
then on this ChildComponent, call
this.props.changeProfile(this.state.values);
Or you can try a state management library too, like Redux or MobX. Personally, I prefer Redux.

Create a reusable React Native Modal Component

I'm going back to basics with React Native, as I feel overwhelmed. I have been looking for an implementation of a reusable modal component. I'm looking for examples of a reusable Modal component in RN? Thanks in advance
You can find many examples of this on StackOverflow. Still, if you need example I can help you with one example. You have mentioned modal component in your question, right?
Your component will look like this with props. let the name be ModalComponent for this file.
render() {
const { isVisible, message, textValue } = this.props;
return (
<Modal
animationType="slide"
transparent={false}
isVisible={isVisible}
backdropColor={"white"}
style={{ margin: 0 }}
onModalHide={() => {}}>
<View>
<Text>textValue</Text>
<Text>message</Text>
</View>
</Modal>
);
}
so now in your js file you need to import this modalComponent and after that, you need to write as
<ModalComponent
isVisible={true}
textValue={'hi there'}
message={'trying to make a basic component modal'}/>
Hope this will help for you
EDIT:
Create seperate components that you want to render inside modal. for Ex: component1.js, component2.js, component3.js with props
component1.js:
render(){
const { textVal, message } = this.props
return (
<View>
<Text>{textVal}</Text>
<Text>{message}</Text>
</View>
)
}
now in ModalComponent
render() {
const { first, second, third, isVisible, component1Text, component1Message } = this.props;
<Modal
animationType="slide"
transparent={false}
isVisible={isVisible}
backdropColor={"white"}
style={{ margin: 0 }}
onModalHide={() => {}}>
<View>
{first && <component1
textValue= component1Text
message= component1Message />}
{second && <Component2 />}
{third && <Component2 />}
</View>
</Modal>
In this way, you can achieve it within the single modal.
You will make a component like this giving the parent component all the liberty to change it through props.
render() {
const { isVisible, message, textValue, animationType, backDropColor, style, onModalHide, children } = this.props;
return (
<Modal
animationType= {animationType || 'slide'}
transparent={transparent || false}
isVisible={isVisible || false}
backdropColor={backdropColor || "white"}
style={[modalStyle, style]}
onModalHide={onModalHide}>
{children}
</Modal>
);
}
Then in your parent component, you need to import this component like this:
import ModalComponent from '../ModalComponent'; //path to your component
<ModalComponent isVisible={true}>
<View>
//any view you want to be rendered in the modal
</View>
</ModalComponent>
I had a lot of troubles using react-native modal, sometimes i started the app and could not close it even when i set the isVisible prop to false, it is even worst on IOs, i did a research and these packages are not being maintained properly.
You will save a lot of time by using a top-level navigator like is recommended in the modal docs: https://facebook.github.io/react-native/docs/modal.
I tried https://github.com/react-native-community/react-native-modal but had the same problems because its an extension of the original react-native modal.
I suggest you to use the react-navigation modal as described here: https://reactnavigation.org/docs/en/modal.html#docsNav
You can refer the following code to write Modal component once and use multiple times.
Write once:
import React, { Component } from 'react';
import { View, Text, Button, Modal, ScrollView, } from 'react-native';
export class MyOwnModal extends Component {
constructor(props) {
super(props);
this.state = {
}
render() {
return(
<Modal
key={this.props.modalKey}
transparent={this.props.istransparent !== undefined ? true : false}
visible={this.props.visible}
onRequestClose={this.props.onRequestClose}>
<View style={{
//your styles for modal here. Example:
marginHorizontal: width(10), marginVertical: '30%',
height: '40%', borderColor: 'rgba(0,0,0,0.38)', padding: 5,
alignItems: 'center',
backgroundColor: '#fff', elevation: 5, shadowRadius: 20, shadowOffset: { width: 3, height: 3 }
}}>
<ScrollView contentContainerStyle={{ flex: 1 }}>
{this.props.children}
</ScrollView>
</View>
</Modal>
);
}
}
Now,
You can call your Modal like following example: (By doing this, you avoid re-writing the Modal and its outer styles everytime!)
Example
<MyOwnModal modalKey={"01"} visible={true} onRequestClose={() =>
this.anyFunction()} istransparent = {true}>
<View>
// create your own view here!
</View>
</MyOwnModal>
Note: If you are in using different files don't forget to import , and also you can pass the styles as props.
(You can create/customise props too based on your requirement)
Hope this saves your time.
Happy coding!
I am a contributor of react-native-use-modal.
This is an example of creating a reusable modal in a general way and using react-native-use-modal: https://github.com/zeallat/creating-reusable-react-native-alert-modal-examples
With react-native-use-modal, you can make reusable modal more easily.
This is a comparison article with the general method: https://zeallat94.medium.com/creating-a-reusable-reactnative-alert-modal-db5cbe7e5c2b

Putting one Component in front of another (higher zIndex) dynamically

So there are two components sort of like this:
<View>
<Component1 />
<Component2 />
</View>
Both, Component1 & Component2 can be dragged and dropped within the View. By default, Component2 will render above the Component1 (since it is above in the stack). I want to make it so that whenever I press Component1 (for drag and drop) it dynamically comes infront of Component2 (Higher zIndex) and if I press Component1 and drag and drop, Component1 comes infront of Component2.
Anyone has any idea on how this can be done?
Edit 1:
I'm using zIndex, but for some reason it's working on iOS but not working on Android. Here's the basic code:
<View>
<View style={{position:'absolute',zIndex:2,backgroundColor:'red',width:500,height:500}}/>
<View style={{position:'absolute',zIndex:1,backgroundColor:'green',width:500,height:500,marginLeft:50}}/>
</View>
Setting dynamic zIndex for child components looks like the way to go. (zIndex on docs)
I would store the zIndexes of each child in the state. And I would wrap Component1 and Component2 with a touchable component if they are not already. When dragging & dropping starts, I'd update the zIndex stored in the state so that the required child would have higher zIndex.
Since I don't exactly know how you structured the components and their layouts, I am unable to provide a example code piece.
EDIT
Workaround for missing zIndex implementation on Android
I'd go something like this, if nothing else works:
import React from 'react';
import {
StyleSheet,
View,
} from 'react-native';
const style = StyleSheet.create({
wrapper: {
flex: 1,
position: 'relative',
},
child: {
position: 'absolute',
width: 500,
height: 500,
},
});
class MyComponent extends React.Component {
constructor(props, context) {
super(props, context);
this.state = {
reverseOrderOfChildren: false,
};
}
render() {
const firstChild = <View style={[style.child, {backgroundColor: 'red'}]} />
const secondChild = <View style={[style.child, {backgroundColor: 'green'}]} />
if (this.state.reverseOrderOfChildren) {
return (
<View style={style.wrapper}>
{secondChild}
{firstChild}
</View>
);
}
return (
<View style={style.wrapper}>
{firstChild}
{secondChild}
</View>
);
}
}