如何在渲染函数错误中访问状态对象

时间:2019-02-12 22:24:22

标签: reactjs react-native redux react-redux

我正在创建一个本机应用程序,并且在渲染中访问状态时遇到问题。

我可以用console.log记录状态(this.state),它会在日志中显示预期的内容。

如果我在console.log中输入类似...(this.state.value)的内容,即使该值存在,也会出错。

我想不通,我整天都在努力!

import React, { Component } from 'react';
import { AsyncStorage, ScrollView, Text, View } from 'react-native';
import { connect } from 'react-redux';
import Loader from '../common/loaders/Loader';
import Header from '../common/header/Header';
import moment from 'moment';
import number from '../../utils/numbers';
import dateLabel from '../../utils/dateLabel';

// Content
import i18n from '../../i18n/i18n';

// Actions
import { loginUser, logoutUser } from '../../actions/authActions';
import { loadingBegin, loadingFinish } from '../../actions/loadingActions';
import { accountsList } from '../../actions/accountsActions';

// Services
import { getAccounts } from '../../services/account';
import { getTransactions } from '../../services/transaction';

// Styles
import common from '../../styles/common';
import styles from './styles';

// --- --- ---
class Home extends Component {

state = {};

constructor(props) {
    super(props);

    if (!this.props.auth.authenticated) this.props.navigation.navigate('Unauthorised');

    this.props.loadingBegin();

    this.state = {
    accounts: [],
    balances: null,
    categories: null,
    transactions: null,
    meta: null,
    };

    this._bootstrapAsync();

    this.props.loadingFinish();
}

componentDidMount = () => {
    const {navigation} = this.props;
    navigation.addListener ('willFocus', () =>{
    console.log('RE-RUNNING PAGE');
    });
}

_bootstrapAsync = async () => {
    // Grab the filter values
    // TODO -> Put filters into Redux store
    this.filters = JSON.parse(await AsyncStorage.getItem('filters'));

    // Check to see if we have any accounts already added
    // Get the accounts info and prime the redux store and state
    const accounts = await getAccounts(this.props.auth);
    this.props.accountsList(accounts);
    this.setState({
    accounts,
    });

    // If there aren't any accounts, redirect to add an account
    if (this.state.accounts && this.state.accounts.length === 0) this.props.navigation.navigate('AccountsNone');

    // Grab the latest transactions and set the state (to be used later)
    let transactionsOptions = {};
    if (this.filters && this.filters.filtersForm) {
    // date set period
    if (this.filters.filtersForm.dates) transactionsOptions.date_type = this.filters.filtersForm.dates;
    // dates between
    if (this.filters.filtersForm.fromDate && this.filters.filtersForm.toDate) {
        transactionsOptions.date_from = this.filters.filtersForm.fromDate;
        transactionsOptions.date_to = this.filters.filtersForm.toDate;
    }
    }
    if (this.filters && this.filters.accountSwitches && this.filters.accountSwitches.length > 0) {
    let obj = this.filters.accountSwitches;
    Object.keys(obj).forEach(key => {
        if (data.accountSwitches[key]) {
        if (!transactionsOptions.account_ids) transactionsOptions.account_ids = {};
        transactionsOptions.account_ids += ',' + key;
        }
    });
    };

    console.log(transactionsOptions);

    let transactions = await getTransactions(this.props.auth, transactionsOptions);
    let meta = transactions.meta;
    let data = transactions.data;

    const balances = this.state.transactions.Balances.map((value) => {                
        return {
            label: moment(value.date, "YYYY-MM-DD").format("MMM Do"), 
            value: value.amount
        }
    });

    const categories = this.state.transactions.Categories;

    this.setState({ 
    transactions: data,
    meta,
    balances,
    categories,
    });
};

render() {
    const { ...props } = this.props;
    const loading = this.props.loading.inProgress;
    let body;

    if (loading) {
    body = <Loader visible={loading} />;
    } else {
    body = (<View>
        <Text style={[styles.balancesDate]}>nuffink</Text>  
    </View>);
    }

    console.log('state.TRANSACTIONS');
    console.log(this.state); // <----------------this works
    console.log(this.state.transactions); // <----------------this doesn't work
    console.log('state.TRANSACTIONS //');

    return (
    <ScrollView
        style={[common.body, styles.container]}
        ref='_main'
        contentContainerStyle={{
        flexGrow: 1
        }}
        stickyHeaderIndices={[0]}
    >

        <Header {...props} txt={"DASHBOARD"} />

        <View style={[common.containerPadding, styles.balances]}>
        <Text>{this.state.trasnactions.value}</Text> <--------------- kills the app and my soul
        </View>
    </ScrollView>
    )
}

}

const mapStateToProps = (state) => {
const { accounts, auth, loading } = state;
return { 
    accounts,
    auth,
    loading
};
};

export default connect(mapStateToProps, { accountsList, loadingBegin, loadingFinish, loginUser, logoutUser })(Home);

2 个答案:

答案 0 :(得分:0)

<Text>{this.state.trasnactions.value}</Text> <--------------- kills the app and my soul

欣赏幽默大声笑。发生的事情是您的组件,就像所有组件将一次渲染而没有任何数据一样,除非在您的状态或道具中已经可用。这意味着您在事件处理程序和componentDidMount中执行的所有逻辑都不会生效,直到组件至少渲染一次。即:在您的方案中没有可用的数据。

这就是为什么当您尝试在初始渲染中访问this.state.transactions.value时,它会杀死您的应用程序,因为开始时没有数据,并且您试图渲染一个假值。

要解决此问题,您可以添加诸如此类的额外逻辑,以检查事务是否真实。

{this.state.transactions ? 
   <Text>{this.state.transactions.value}</Text>
: null }

答案 1 :(得分:0)

这是因为this.state.transactions为null开头(在构造函数中设置),并且您无法访问未定义内容的属性。另外,您在<Text/>内的渲染函数中拼写了“交易”。

解决此问题的一种方法是在渲染中检查尝试访问this.state.transactions之前是否已定义this.state.transactions.value,例如

{this.state.transactions && <Text>{this.state.transactions.value}</Text>}