Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I render a React Native component using Async Storage data on start up?

I want to load from AsyncStorage and use it during my react native component rendering but my component renders immediately on app start and doesn't give AsyncStorage time to return data.

How can I use AsyncStorage so my components will either A) wait to be rendered until they have the correct information or B) change to reflect the correct information once the data has been loaded?

    import React from 'react';
    import { AsyncStorage } from 'react-native';

    // my wrapper object around AsyncStorage
    export default class StorageManager  {

    constructor() {
        this.data = {
            settings: { },
        }
        this.loadSettings();
    }

    async loadSettings() {
        await AsyncStorage.getItem('settings')
            .then( (settings) => this.data.settings = JSON.parse(settings) )
            .catch( (err) => console.log(err) );
    }

    /*
     * settings = {days: number, currency: string, costPerDay: number }
     */
    getDays() {
        return this.data.settings.days;
    }
}

And my component code. Here AchievementScreen is rendered first and the StorageManager doesn't have time to load anything. The other screens are rendered when I tab over to them so they have everything loaded correctly.

import React, {Component} from 'react';
import {AppRegistry, StyleSheet, Alert} from 'react-native';
import { Container, Header, Content, Body, List, Text} from 'native-base';
import { Col, Row, Grid } from "react-native-easy-grid";
import AchievementCard from '../../components/AchievementCard/AchievementCard'

export default class AchievementScreen extends Component {
    constructor(props) {
        super(props);
        // debugger shows props.storageManager as well-shaped but empty.
        this.state = {
            storageManager: this.props.storageManager,
            achievementCards: [ this.moneySavedAchievement(props.storageManager),
                               {header: "Header 2", body: "Body 2", icon: "money"},
                            ]
        }
    }

    moneySavedAchievement(storageManager) {
        return {
            header: "Money Saved",
            body:   storageManager.data.settings.currency + 
                    storageManager.data.settings.costPerDay *
                    storageManager.data.settings.days,
            icon: "money"
        }
    }

    render() {
        return (
            <Container>
            <Content>
            <Grid>
                <Col>
                    <List   dataArray={this.state.achievementCards.slice(0, this.state.achievementCards.length / 2)}
                            renderRow={ (item) => 
                                <AchievementCard achievementHeader={item.header}
                                    achievementBody={item.body}
                                    achievementIcon={item.icon} />
                            }
                    />
                </Col>
                <Col>
                    <List   dataArray={this.state.achievementCards.slice(this.state.achievementCards.length / 2)}
                            renderRow={ (item) => 
                                <AchievementCard achievementHeader={item.header}
                                    achievementBody={item.body}
                                    achievementIcon={item.icon} />
                            }
                    />
                </Col>
            </Grid>
            </Content>
        </Container>
    );
    }
}

AppRegistry.registerComponent('AchievementScreen', () => AchievementScreen);

My frontpage app code

import React, {Component} from 'react';
import {AppRegistry, StyleSheet} from 'react-native';
import {Tabs, Tab, Container, Header, Title, Body, TabHeading, Text, Right, Left, Button, TouchableOpacity} from 'native-base';
import Icon from 'react-native-vector-icons/FontAwesome';

import HomeScreen from './app/screens/HomeScreen/HomeScreen';
import MilestoneScreen from './app/screens/MilestoneScreen/MilestoneScreen';
import AchievementScreen from './app/screens/AchievementScreen/AchievementScreen';
import StorageManager from "./app/modules/StorageManager/StorageManager";

export default class my_react_app extends Component {
    constructor() {
        super();
        this.state = {
            storageManager: new StorageManager()
        }
    }

    render() {
        return (
            <Container>
            <Header hasTabs>
            <Left />
                <Body>
                    <Title>My App</Title>
                </Body>
                <Right>
                    <Icon active name="ellipsis-v" color="white"/>
                </Right>
            </Header>
            <Tabs initialPage={0}>
                <Tab heading="Status">
                    <AchievementScreen storageManager={this.state.storageManager} />
                </Tab>
                <Tab heading="Progress">
                    <HomeScreen storageManager={this.state.storageManager} />
                </Tab>
                <Tab heading="Milestones">
                    <MilestoneScreen storageManager={this.state.storageManager} />
                </Tab>
            </Tabs>
            </Container>
        );
    }
}

AppRegistry.registerComponent('my_react_app', () => My_React_App);
like image 971
Atomar94 Avatar asked Feb 23 '26 22:02

Atomar94


1 Answers

An immediate problem which I can see is that AchievementScreen component is dependent on storageManager, so the only way the component can re-render( from an external effect ) is if the storageManager prop changes, which does not happen.

This line -

<AchievementScreen storageManager={this.state.storageManager} />

Also if you want AchievementScreen to re-render on data fetches, a better design would be to pass the data to AchievementScreen rather than passing an object which in-turn gets data. AchievementScreen should be unaware of where the data is fetched from. ( For better side-effect management look at libraries like redux, redux-thunk, redux-saga )

So your Ideal implementation would be -

componentDidMount(){
  this.storageManager.getData().then((data) => 
   this.setState({moneySavedAchievement: data});
 );
}

<AchievementScreen moneySavedAchievement={moneySavedAchievement} />

Hope this helps!

like image 98
Shobhit Chittora Avatar answered Feb 26 '26 12:02

Shobhit Chittora