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

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

Related

How can I get SetState to stop overwriting my component tags

I am using these tags in my react native app: https://github.com/peterp/react-native-tags
Being a react rookie I have a problem with SetState. I have set-up the tags like this:
<Tags
initialText=""
textInputProps={{
placeholder: "Servicable Postcodes"
}}
initialTags={["3121"]}
onChangeTags={(tags) => this.changeTags(tags)}
inputStyle={{ backgroundColor: "white", borderBottomWidth: 1 }}
renderTag={({ tag, index, onPress, deleteTagOnPress, readonly }) => (
<TouchableOpacity key={${tag}-${index}} onPress={onPress}>
<Text style={styles.tagStyle}>{tag}</Text>
</TouchableOpacity>
)}
/>
changeTags(newtags) {
//this.setState({ myTags: newtags.toString()});
var tagList = newtags.toString();
/*this.setState({myTags: tagList}, function () {
console.log(this.state.myTags);
});*/
}
Adding tags works like this [3121] [1111] [2222]
So that code works well. As soon as I uncomment the setState it will run everything like normal but the tag will not get added to the list. So if I add 1111 my console log will be 3121, 1111 (But 1111 will not show as as a tag) then if i try to add another it will be 3121, 2222 etc.
I think because it re-renders with setState the tag is never added/overwritten and I just keep getting left with the single 3121 tag. Or maybe i'm a react noob and have no idea.
Any help is appreciated.
Thanks
You are right in saying that each render will cause the Tag component to reset back to 3121.
To avoid this you need to store your current tags in your state like this:
export default class App extends React.Component {
state = { tags: ['3121'], myTags: "3121" }; // This line
render() {
return (
<View style={styles.container}>
<Tags
initialText=""
textInputProps={{
placeholder: 'Servicable Postcodes',
}}
initialTags={this.state.tags} // This line
onChangeTags={tags => this.changeTags(tags)}
inputStyle={{ backgroundColor: 'white', borderBottomWidth: 1 }}
renderTag={({ tag, index, onPress, deleteTagOnPress, readonly }) => (
<TouchableOpacity key={`${tag}-${index}`} onPress={onPress}>
<Text style={styles.tagStyle}>{tag}</Text>
</TouchableOpacity>
)}
/>
</View>
);
}
changeTags(newtags) { // This function
this.setState({ tags: newtags, myTags: newtags.toString() }, () =>
console.log(this.state)
);
}
}
This means for each render (which will happen every time a tag is added as you are calling setState), the Tag component will render the tags you have stored in the state.
I have also included another member in your state called myTags, which maps to the string value of your tags as it seems like this was necessary from your previous code.
Here is a working snack

create styled component with third party component that has custom style props?

Im using the Slider from react-native-community the component has unique props used to style it, thumbTintColor, minimumTrackTintColor, maximumTrackTintColor, which I need to pass styles to from my themeprovider. It seems I cant pass props to this component, anyone aware of a way to achieve this? This is what I have now, and its unfortunately not working.
const StyledSlider = styled(Slider)``
export const SurveySlider = props => {
return (
<StyledSlider
style={{
width: '80%',
height: 40,
alignSelf: 'center'
}}
minimumValue={0}
maximumValue={20}
thumbTintColor={`${props => props.theme.primaryColor}`}
minimumTrackTintColor={`${props =>
props.theme.primaryColor}`}
maximumTrackTintColor={`${props =>
props.theme.primaryColor}`}
onValueChange={value => props.onValueChange(value)}
/>
)
}
try this:
minimumTrackTintColor={props.theme.primaryColor}
I was able to solve this by using the attrs method supplied by styled components
const StyledSlider = styled(Slider).attrs(props=>{
thumbTintColor: props.theme.primaryColor
})``

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.

Passing State to another Component in 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

How to override style in NativeBase?

In every code example mentioned in NativeBase Docs, there's no usage of React StyleSheet.
I didn't find a way to override styles of NativeBase.
How can I include React StyleSheet into my app?
NativeBase uses a different approach to add styling to your component.
It uses a Utility first approach, wherein it leverages the ReactNative stylesheets API and adds a layer of Utility Props on top of it. The idea is to pass props to your component to change their styling, instead of defining a className and adding styles in a separate stylesheet.
For example, with React Native, you would write:
function Example() {
return (
<View style={styles.container}>
{/* content */}
</View>;
);
}
const styles = StyleSheet.create({
container: {
backgroundColor: '#0891b2',
paddingVertical: 16,
paddingHorizontal: 12,
borderRadius: 5,
alignSelf: 'center',
width: 375,
maxWidth: '100%'
}
});
Now, with NativeBase, to achieve the same view, you would write it as follows:
function Example() {
return (
<NativeBaseProvider>
<Box bg="primary.600" py="4" px="3" rounded="md" width={375} maxWidth="100%">
{/* content */}
</Box>
</NativeBaseProvider>;
);
}
Here, NativeBase has already defined bg, py, px, etc. as props and mapped them to the styling properties of backgroundColor, paddingVertical , paddingHorizontal, etc. respectively.
There is a list of these mappings provided here, which you can simply refer to add any styling that you may need for your component.
For NativeBase usually if you want customize the appearance you need to do it at a theme level and not a component/widget level.
http://nativebase.io/docs/v0.5.2/customize
I believe the reason it this keeps the UI consistent.
Have a look at the source code for all the widgets that you are trying to override the style for:
https://github.com/GeekyAnts/NativeBase/blob/master/Components/Widgets/
There are certain cases where you can amend certain aspects e.g. for the button
this.props.style is used anywhere but this.props.textStyle
So you can override the textStyle when you define the component:
<Button textStyle={{...}}/>
https://github.com/GeekyAnts/NativeBase/blob/master/Components/Widgets/Button.js