How to call an async method in render method from Expo? - react-native

I saved data to AsyncStorage. Now I want to show all data from AsyncStorage in separate screen.
Method getData is async method. It reads from AsyncStorage.
I use code like that
import React from "react";
class List extends React.Component {
state = { list: null };
async componentDidMount() {
const list = await getData("List");
console.log('LIST: ' + JSON.stringify(list));
this.setState({ list });
}
render() {
const { list } = this.state;
console.log('state: ' + JSON.stringify(list));
if(list || list.length <= 0)
return (<View><Text>Empty.</Text></View>);
return (
<View>
{ list.map(item => (
<Text tabLabel={item}>{item}</Text>
))}
</View>
);
}
}
When I run that code, i get 2 console messages:
state: []
and
LIST: [{item1}, {item2}...]
It means that componentDidMount fires after render method, that is why UI is empty.
How can I change this. I need to read data from AsyncStorage and show it in UI.
Thank you.

componentDidMount as the title says is meant to run after render. To achieve what you want to do you can use componentWillMount.
import React from 'react';
class List extends React.Component {
state = { list: null };
componentWillMount() {
const loadData = async () => {
const list = await getData('List');
console.log('LIST: ' + JSON.stringify(list));
this.setState({ list });
};
loadData();
}
render() {
const { list } = this.state;
console.log('state: ' + JSON.stringify(list));
if (list || list.length <= 0)
return (
<View>
<Text>Empty.</Text>
</View>
);
return (
<View>
{list.map((item) => (
<Text tabLabel={item}>{item}</Text>
))}
</View>
);
}
}

Related

Navigating and submitting form properties

I am trying to navigate away from a screen and submit info with a handleSubmit.
export default class CreditRegister extends Component {
handleSubmit = () => {
const navigation = navigate(this.handleSubmit)
const value = this._form.getValue();
console.log('value: ', value);
}
render() {
return (
<>
onPress={() => this.props.navigation.navigate('HomeAddress')}
If you want to call your handlesubmit fnc into your Homeaddress screen then you can pass the callback function like this.
export default class CreditRegister extends Component {
handleSubmit = () => {
// your code
}
render() {
return (
<>
onPress={() => this.props.navigation.navigate('HomeAddress',{handleSubmit:this.handleSubmit})}
<>
then in your HomeAddress screen do it like this
export default class HomeAddress extends Component {
render() {
const {handleSubmit}=this.props.route.params
return (
<>
onPress={handleSubmit}
<>

React Native : executing function multiple times when pressing a button

What I want to do is executing function multiple times when pressing the button.
<Button onPress={()=>{console.log('execute')}}/>
If I use onPress, the function is executed only once, even if I does not press out.
But I hope console.log is executed multiple times in sequence (or every seconds) until press out.
You can use TouchableOpacity. In this example addOne function will execute every second.
export default class App extends Component{
constructor() {
super();
this.state = {
number: 0,
};
this.timer = null;
this.addOne = this.addOne.bind(this);
this.stopTimer = this.stopTimer.bind(this);
}
addOne() {
this.setState({number: this.state.number+1});
this.timer = setTimeout(this.addOne, 1000);
}
stopTimer() {
clearTimeout(this.timer);
}
render(){
return (
<View style={styles.container}>
<TouchableOpacity onPressIn={this.addOne} onPressOut={this.stopTimer}>
<Text>Press me</Text>
<Text>{this.state.number}</Text>
</TouchableOpacity>
</View>
);
}
}
This is how I track how many times someone pressed with hooks:
const [lastPressed, setLastPressed] = useState(0);
const [pressedAmount, setPressedAmount] = useState(0);
const handlePress = useCallback(() => {
const time = new Date().getTime();
const delta = time - lastPressed;
setLastPressed(time);
if (lastPressed) {
if (delta < DOUBLE_PRESS_DELAY) {
setPressedAmount(pressedAmount + 1);
} else {
setPressedAmount(0);
}
}
}, [lastPressed]);

State changes in a react application with mobX

My task is to show the download component when data from the server has not yet arrived.
export const LoaderComponent = () => (
<View style={styles.center}>
<ActivityIndicator size="large" />
</View>
);
const styles = StyleSheet.create({
center: {
.....
},
});
I created a state file to display the boot component.
import { observable, action } from 'mobx';
class LoaderState {
#observable loading: boolean = true;
#action showLoader() {
this.loading = true;
}
#action hideLoader() {
this.loading = false;
}
}
export default new LoaderState();
When authorizing the user, I display the download component, after receiving data from the server, I hide the download component. I made an artificial delay of two seconds.
class AuthState {
#observable email: string = '';
#observable password: string = '';
#action authentication(data: IAuth) {
console.log('Action authentication');
LoaderState.showLoader();
....
setTimeout(() => {
LoaderState.hideLoader();
console.log('Change state loader', LoaderState.loading);
}, 2000);
}
}
export default new AuthState();
On the next screen, I check if the download flag is set, I show the download component, and if not, I hide it.
export const ProvidersScreen = () => {
console.log(LoaderState.loading);
if (LoaderState.loading) {
return <LoaderComponent />;
}
return (
<View>
....
</View>
);
};
The problem is that the download component is always shown and when the state changes, it is not hidden. Why is the download component not hiding?
I think the reason is your ProvidersScreen is not an observer component, so try it:
export const ProvidersScreen = observer(() => {
console.log(LoaderState.loading);
if (LoaderState.loading) {
return <LoaderComponent />;
}
return (
<View>
....
</View>
);
});
You forgot to add observer
Add below code:
import { observer } from "mobx-react";
export const ProvidersScreen = observer(() => {
console.log(LoaderState.loading);
if (LoaderState.loading) {
return <LoaderComponent />;
}
return (
<View>
....
</View>
);
});

How do I update Different Screens form another independent screen?

I have an App with a navigationbar with 2 Screens.
When i apply a function on Screen/Component 1 , I want to render or trigger a change in the Second Screen.
is there a way to either re-render the screen on Enter or to update the state of the other screen ?
Component one:
export default class HomeScreen extends React.Component {
constructor() {
super();
}
_onPress(){
try {
await AsyncStorage.setItem('value', 'changed Value');
} catch (error) {
console.log(error.message);
}
console.log("saved: " + this.state.userName )
}
render() {
return (
<View style={styles.container}>
<Button title="btn" onPress={() => this._onPress()} >
</Button>
</View>
)
}
component 2:
export default class SecondScreen extends React.Component {
constructor() {
super();
this.state = {some : ''}
}
async getValue () {
let recievedValue = '';
try {
let promise = await AsyncStorage.getItem('value') || 'cheeseCake';
promise.then((value) => recievedValue = value)
} catch (error) {
// Error retrieving data
console.log(error.message);
}
return recievedValue
}
render() {
var value= this.getValue();
return (
<View style={styles.container}>
<Text>
HERE CHANGED VALUE: {value}
</Text>
<Button onPress={()=> this.setState((prev)=> {some:'Thing'})}>
</Button>
</View>
)
}
When i press the Button on screen 1(HomeScreen) the value is saved.
But it only shows in the secont screen when I trigger a statechange via Button Press.
How do I render the screen when I visit the screen via navigation bar ?
Did you try EventEmiter?
Use this custom event listener: https://github.com/meinto/react-native-event-listeners
eg:
import { EventRegister } from 'react-native-event-listeners'
/*
* RECEIVER COMPONENT
*/
class Receiver extends PureComponent {
constructor(props) {
super(props)
this.state = {
data: 'no data',
}
}
componentWillMount() {
this.listener = EventRegister.addEventListener('myCustomEvent', (data) => {
this.setState({
data,
})
})
}
componentWillUnmount() {
EventRegister.removeEventListener(this.listener)
}
render() {
return <Text>{this.state.data}</Text>
}
}
/*
* SENDER COMPONENT
*/
const Sender = (props) => (
<TouchableHighlight
onPress={() => {
EventRegister.emit('myCustomEvent', 'it works!!!')
})
><Text>Send Event</Text></TouchableHighlight>
)

React Native - Component update parent

I'm making an app in react native and I'm facing a little problem.
I finished the first layout and now I want to change the style all over the app with a second layout
This is what I have in my parent.
As you can see I use AsyncStorage to check when you open again the app the last selected layout. It all working perfectly.
export default class Home extends React.Component
{
constructor(props){
super(props);
this.state = {
view:0
}
}
componentWillMount()
{
this.checkStructureView();
}
checkStructureView = async() =>
{
const StructureView = await
AsyncStorage.getItem('#StructureView');
if(StructureView == 1)
{
this.setState({
view:1
})
}
else
{
this.setState({
view:0
})
}
}
render()
{
if(this.state.view == 1)
{
return(
<ChangeView/>
...
)
}
else
{
return(
<ChangeView/>
...
)
}
}
}
And this is my component ChangeView. It's a little bit messy because I have for each button active/inactive styles. This is also working perfectly, but the problem is that when I click on the button to change the layout will not change it, only after I refresh the app.
First I added this inside the parent and after I updated the state, the layout has changed instantly but I have more pages where I need to add this component, that's why I'm using an component.
So my question is how can I update instantly the parent state so my layout changes every time I click on the component button without reloading the app.
import React, { Component } from 'react'
import {
View,
Text,
Image,
TouchableOpacity,
AsyncStorage
} from 'react-native'
export default class ChangeView extends Component {
constructor(props){
super(props);
this.state = {
position: this.props.position,
view:0,
view1:require(`../assets/icons/view1_inactive.png`),
view2:require(`../assets/icons/view2_active.png`)
}
}
componentDidMount()
{
this.checkViewStructure();
}
checkViewStructure = async()=>
{
const StructureView = await AsyncStorage.getItem('#StructureView');
if(StructureView == '0')
{
this.setState({
view1:require(`../assets/icons/view1_inactive.png`),
view2:require(`../assets/icons/view2_active.png`)
})
}
else
{
this.setState({
view1:require(`../assets/icons/view1_active.png`),
view2:require(`../assets/icons/view2_inactive.png`)
})
}
}
changeToList = async() =>
{
const StructureView = await AsyncStorage.getItem('#StructureView');
if(StructureView == '0')
{
await AsyncStorage
.setItem('#StructureView', '1')
.then( () => {
//
})
.catch( () => {
alert('Something happened! Please try again later.');
});
this.setState({
view1:require(`../assets/icons/view1_active.png`),
view2:require(`../assets/icons/view2_inactive.png`)
})
}
}
changeToPics = async() =>
{
const StructureView = await AsyncStorage.getItem('#StructureView');
if(StructureView == '1')
{
await AsyncStorage
.setItem('#StructureView', '0')
.then( () => {
//
})
.catch( () => {
alert('Something happened! Please try again later.');
});
this.setState({
view1:require(`../assets/icons/view1_inactive.png`),
view2:require(`../assets/icons/view2_active.png`)
})
}
}
render()
{
if(this.state.position === 0)
return(
<View style={{alignItems:'flex-end',marginTop:20,marginBottom:10,justifyContent:'flex-end',flexDirection:'row'}}>
<View>
<TouchableOpacity
onPress= {() => this.changeToList()}
>
<Image
source={this.state.view1}
style={{width:15,height:21,margin:5}}
/>
</TouchableOpacity>
</View>
<View>
<TouchableOpacity
onPress= {() => this.changeToPics()}
>
<Image
source={this.state.view2}
style={{width:15,height:21,margin:5}}
/>
</TouchableOpacity>
</View>
</View>
)
else
return null
}
}
The ChangeView component only changes state in that specific component. There are several ways of propagating change to the parent component. One way is to implement an onChange prop for the ChangeView component. Your Home component render function would then look like something like this:
render() {
if(this.state.view == 1) {
return(
<ChangeView onChange={ (view) => this.setState({ view }) } />
...
)
} else {
return(
<ChangeView onChange={ (view) => this.setState({ view }) } />
...
)
}
}
You can read more about props here: https://reactjs.org/docs/typechecking-with-proptypes.html
There are other ways of doing this if you have state handler for your application such as Redux.