this.state within react-navigation screen - react-native

I'm having trouble accessing and defining state. My app uses react-navigation. Overall within my app I can always work with state (without errors), but within the primary tabbar screens, I get a "null is not an object" error when I use a simple definition of state like I do below.
I am not using redux
export class Review_Screen extends React.Component {
// set title at the top of the page
static navigationOptions = ({navigation}) => ({
title: navigation.state.params.title
});
constructor(props) {
super(props);
const { params } = this.props.navigation.state;
this.state = {
// general ID info
barcode: params.productdata.barcode,
userID: params.user.ID,
username: params.user.name,
expanded: false,
testing: 'hallo hallo',
};
}
render() {
console.log('-_-_-_-_-_-_-_-_-_-_-_-_-');
console.log(this.state.testing);
console.log('-_-_-_-_-_-_-_-_-_-_-_-_-');
const { params } = this.props.navigation.state;
etc...
// results in error "null is not an object (evaluating 'this.state.testing')
I assume this is because I am supposed to work differently with state when I am within react-navigation.
How do I define some local state variables? What is best practice? I will at some point go to redux, but am not ready for that yet.

Well, that's embarrassing. The issue arose because I had my react-native on "hot reloading", while I was coding.
This basically means that I was asking for a variable that wasn't defined, because the constructor wasn't called (the app was live while coding).
Once I restart or reload the app, then it does define the state (because while initially loading the app it calls the constructor().
Hope this serves someone else..

Related

safe alternative to UNSAFE_componentWillMount?

I have an app that uses class based components. In the process of converting it from flow to typescript in line with current trends I realised that componentWillMount has ben deprecated and is no longer safe to use. I'm not sure what to do with it as this is in a class based component and converting it all into function components will take too long. I tried componentDidMount, but I got an error saying that setState should not be used in componentDidMount:
UNSAFE_componentWillMount() {
this._setupPanResponder();
this.setState({ date: this.props.date });
}
Is there a safe alternative to this that doesn't involve changing the component to a function component?
Can you move it to constructor?
Something like:
...
constructor(props: PropsType) {
super(props);
this._setupPanResponder();
this.state = { date: props.date };
}
...

Localization of React Native navigators

I am building an app where the users are prompted to choose their language the first time they launch the app. The language is then stored locally using AsyncStorage.
Every time the app launches the language is retrieved from storage and saved into the global.lang variable to be used by all components:
AsyncStorage.getItem('settings', (err, item) => {
global.lang = item.lang;
});
When I use the global.lang variable in the render() method in any component everything seems to be ok. However I run into trouble when trying to use the same variable when initializing my navigators:
const TabNavigator = createBottomTabNavigator(
{
Home: {
screen: HomeScreenNavigator,
navigationOptions:{
title: strings['en'].linkHome, --> this works
}
},
News: {
screen: NewsScreen,
navigationOptions:{
title: strings[global.lang].linkNews, --> this fails
}
}
});
I believe that this because the value is not retrieved from AsyncStorage by the time that the navigators are constructed. If I set the global.lang manually (eg. global.lang = 'en';) it seems to be OK, but not when I try to retrieve it from the storage.
Is there something that I am missing? Could I initialize the navigator with a default language and change the title later based on the value retrived?
Any help would be greatly appreciated.
The navigators are constructed in the app launch. So you would need to use some placeholder text and use the method described here where you change all screen titles based on the screen key...
Or... this sounds insane and i have never tried it. But you can use a loading screen where you retrieve the languaje settings. then... via conditional rendering you "render" a navigator component . Idk if it would work the same way , but you can try it. below some code that i just created for this purpose
export default class MainComponent extends React.Component {
constructor(props) {
super(props);
this.state = { hasLanguage:false};}
componentDidMount(){
this.retrieveLanguage()
}
async retrieveLanguage(){
//await AsyncStorage bla bla bla
//then
this.setState({hasLanguage:true})
}
render() {
return (
{
this.state.hasLanguage?
<View>
//this is a view that is rendered as a loading screen
</View>:
<Navigator/>//this will be rendered, and hence, created, when there is a language retrieved
}
);
}
}
Again. I don't know if react navigation creates the navigator at render . If so. When it creates a navigator , there should be the languaje to be used there

React Native - What's the different about this.state.data with this.data?

I was confused what's the different about this.state.data with this.data
let say I have a code like this:
componentWillMount(){
console.log(this.props.navigation.state.params.list);
api.get('my API Url')
.then((response)=> {
this.setState({data: JSON.parse(response.data)[0]})
this.data=JSON.parse(response.data)[0]
})
.catch((err)=>{
console.log("axios catching error")
Alert.alert("failed", "Retry to retrieve from API", [{text:'OK', onPress:()=>{this.componentWillMount()}}])
console.log(err)
})
}
constructor(props){
super(props);
this.state ={ data:[] }
this.data=[]
}
class Visit extends React.Component {
render() {
if (this.data.length==0){
return(
<Loader/>
)
}
return (
<Text>Visit</Text>
);
}
}
export default Visit;
with above code, I can't render <Text>Visit</Text> when this.data already have an array, but with this.state.data my App can rendering <Text>Visit</Text>,
so I want to know the different about this.state.data with this.data,
anyone can explain me?
this.state.data refers to data property of the current class state, while this.data refers to data property of current class.
A component's State is a trivial part of React environment, and if you can't understand this difference I suggest you to check this.
I also suggest you to check react official docs, almost everything from React applies to React Native, for me, React Native is almost the same as React, the only difference is you have more limited components in RN. We could say that RN is a subset of React
Note: property or properties it's not the same than component's props defined in React specs
For the purpose of React, it would be best to make use of the state. this.state.data refers to the data prop stored in the current component state. You can set the value by calling the setState method. On the other end, this.data refers to the data prop of the current class. You could change its value like you would other class variables in Javascript.

Passing updated state to react-navigation screen

How can I pass new state to a React Navigation function?
My code currently looks like this:
Simplified view of my parent class:
constructor(props){
super(props)
this.state = {
code: "aaa"
}
this.refresh = this.refresh.bind(this)
}
refresh() {
this.setState({
code: "bbb"
})
}
async componentDidMount(){
const {navigate} = this.props.navigation
navigate("Child", {screen: "Screen Two", code: this.state.code, refresh: this.refresh})
}
In the child class I then do the following:
this.props.navigation.state.params.refresh()
The issue I am facing:
Option 1: If I have the code as it currently is, it will not pass the new state value to the navigator because it is not in the render function
Option 2: If I place the code in the render function, it gives me the warning: "Cannot update during an existing state transition".
What am I doing wrong and how can I fix this?
Further details
I am using this main screen to load some of the details from an API on the web and store them in state. I want to be able to pass a refresh function to the second screen that I will be able to use to reload data from the API onto the main screen. Once the data is loaded back into the state on the main screen it should propagate back down to the second screen. This seems easy to do without using a navigator, but I am not sure how to do it with a navigator.
I am not currently wanting to use redux due to the learning curve, but would like to look into it some time in the future.
So you are trying to call refresh() method inside your child component. If you use this inside render function the refresh() method will be called repeatedly and it will give a warning: "Cannot update during an existing state transition".
If you keep the code as it is, it will update the parent class state. But that update will not be reflected when you accessing this.props.navigation.state.params.code. This will only give the value 'aaa'.
Option 1;
You can use redux and easily handle this scenario.
Option 2;
If you really want to know the value of the parent class state you can pass a function as navigation params to child which will return the value of the state.
Parent class.
constructor(props){
super(props)
this.state = {
code: "aaa"
}
this.refresh = this.refresh.bind(this);
this.getState = this.getState.bind(this)
}
refresh() {
this.setState({ code: "bbb" })
}
getState() {
return this.state.code;
}
async componentDidMount(){
const {navigate} = this.props.navigation
navigate("Child", {screen: "Screen Two", code: this.state.code, refresh: this.refresh, getState: this.getState })
}
Inside your child class use the following code to get the parent class state.
let parentClassState = this.props.navigation.state.params.getState();

What is the <{}> syntax after extends Component?

I started a new project today using React Native 0.51.0 and noticed that the class syntax for the default project file had something new added, the <{}> syntax after extends Component:
export default class App extends Component<{}> {
...
}
I tried doing research but most search engines ignore special characters even with exact string matching, so trying to find out what this syntax is has proved to be difficult. I did some testing and was able to figure out that this change appeared in v0.49.0. The release notes make no mention of what this added syntax does though.
A lot of vague keyword searching and reading leads me to believe that this may be syntax related to TypeScript, but being unfamiliar with the language, I'm at a loss as to how to search and find out more about the syntax without knowing what the proper term for it is. Could anyone tell me what the name of the syntax and what it does? Specifically with regards to React Native.
It is related to Flow typings for the props you will receive in the component. Component<{}> would mean that you don't expect the component to receive props.
With Flow and React.Component, you can define types for props and state (see React$Component type declaration for details).
Example from Flow documentation about React components
import * as React from 'react';
type Props = { /* ... */ };
type State = {
count: number,
};
class MyComponent extends React.Component<Props, State> {
state = {
count: 0,
};
componentDidMount() {
setInterval(() => {
this.setState(prevState => ({
count: prevState.count + 1,
}));
}, 1000);
}
render() {
return <div>Count: {this.state.count}</div>;
}
}
<MyComponent />;