如何在启动时使用Async Storage数据渲染React Native组件?

3
我希望从AsyncStorage中加载数据并在我的React Native组件渲染期间使用它,但我的组件会立即在应用启动时呈现,没有给AsyncStorage返回数据的时间。
如何使用AsyncStorage使我的组件要么A)等待正确的信息被渲染或B)一旦数据加载完毕就改变以反映正确的信息?
    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;
    }
}

以下是我的组件代码。首先呈现的是AchievementScreen,此时StorageManager没有时间加载任何内容。当我切换到其他屏幕时才会呈现它们,因此它们都已正确加载好了。

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);

我的主页应用程序代码

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);
2个回答

1

我看到的一个直接问题是AchievementScreen组件依赖于storageManager,因此组件可以重新渲染(来自外部影响)的唯一方法是如果storageManager prop发生更改,而这并没有发生。

这行代码 -

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

此外,如果您希望AchievementScreen在数据获取时重新渲染,则更好的设计是将数据传递给AchievementScreen,而不是传递一个对象,该对象再获取数据。AchievementScreen应该不知道数据从哪里获取。(为了更好地管理副作用,请查看像redux、redux-thunk、redux-saga这样的库)
因此,您的理想实现应该是 -
componentDidMount(){
  this.storageManager.getData().then((data) => 
   this.setState({moneySavedAchievement: data});
 );
}

<AchievementScreen moneySavedAchievement={moneySavedAchievement} />

希望这有所帮助!

0
正如您在问题中所述,您的AchievementScreen组件在storageManager检索数据之前被渲染。实际上,您正在构造函数中明确设置状态:
this.state = { storageManager: this.props.storageManager }
这为this.state.storageManager设置了值,当storageManager更新数据时,AchievementScreen组件不知道该怎么做。
您可以在组件中添加componentWillReceiveProps生命周期钩子,每次组件接收到新的props时都会调用它。因此,您可以执行以下操作:
在构造函数下的AchievementScreen中:
componentWillReceiveProps(nextProps) {

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接