无法从 Axios api 调用设置 class 变量

Unable to set class variable from Axios api call

我正在尝试在 fetchScoresCall 中同时设置 inPlayScores 和 finalScores。

我可以看到它在那个调用中像它应该的那样填充但是一旦在那个调用之外对于所有数据仍然是空的。

我猜我遗漏了一些相对简单的东西,因为我是 React 开发的新手,所以任何帮助或对我做错了什么的解释都将不胜感激。

import React, { PureComponent } from 'react'
import { View, Text, Easing } from 'react-native'
import TextTicker from 'react-native-text-ticker'
import { globalStyles } from '../styles/global'

export default class ScoreRunner extends PureComponent {
    constructor(props) {
        super(props);

        initialState = {
            leagues: ['NBA', 'NFL', 'NHL'],
            scores: null,
        };
        this.state = initialState;
        this.loopForFetch = this.loopForFetch.bind(this);
        this.fetchScoresCall = this.fetchScoresCall.bind(this);
        this.child = null;

    }

    componentDidMount() {
        this._asyncRequest = this.loopForFetch(this.state.leagues);
    }

    inPlayScores = {
        NBA: null,
        NFL: null,
        NHL: null,
    }
    finalScores = {
        NBA: null,
        NFL: null,
        NHL: null,
    }

    loopForFetch = (leagues = ['NBA', 'NFL', 'NHL']) => {
        endPoint = '';
        leagues.forEach( async league => {
            switch(league) {
                case 'NBA':
                    endPoint = "basketball";
                    this.fetchScoresCall(league, endPoint);
                    break;
                case 'NFL':
                    endPoint = "American_Football";
                    this.fetchScoresCall(league, endPoint);
                    break;
                case 'NHL':
                    endPoint = "Ice_Hockey";
                    this.fetchScoresCall(league, endPoint);
                    break;
            }
        });
        console.log('finalScores are null here? ');
        console.log(this.finalScores);
        const strStr = <>
                {console.log('finalScores are null here as well?')}
                {console.log(this.finalScores)}
            <Text style={{color: 'green'}}>
                {this.inPlayScores['NBA']}
                <Text style={{color: 'red'}}>
                    {this.finalScores.NBA}
                </Text>
                {this.inPlayScores['NHL']}
                <Text style={{color: 'red'}}>
                    {this.finalScores['NHL']}
                </Text>
                {this.inPlayScores['NFL']}
                <Text style={{color: 'red'}}>
                    {this.finalScores['NFL']}
                </Text>
            </Text>
        </>

        this.setState({ scores: strStr}, function() {
                // do something with new state
            
            this.setState(this.state);
        });
        this.setState.scores = strStr;
    }

    fetchScoresCall = async (league = null, endPoint = null) => {
                console.log('do we have a this.state.finalscores?');
                console.log(this.finalScores);
        if (league && endPoint) {
            console.log('league: ' + league + ' : endpoint: ' + endPoint);
            await fetch("https://www.sportsscoredatabase.com/api/v2/json/" + this.state.apiKey + "/livescore.php?s=" + endPoint, {
            "method": "GET"
            })
            .then(response => response.json())
            .then(response => {
                // console.log(response);
                inPlayStr = ' :::' + league + '::: In Play: ';
                finalStr = ' :::' + league + '::: Final Scores: ';
                response["events"].forEach(score => {
                    if (score["strLeague"] === league && score["intHomeScore"] && score["intAwayScore"]) {
                        if (!["NS", "POST", "CANC", "FT"].includes(score["strStatus"])) {
                            inPlayStr += "  H:" + score["strHomeTeam"] + "(" + score["intHomeScore"] + ")" + " vs. A:" + score["strAwayTeam"] + "(" + score["intAwayScore"] + ")  |"  
                        } else if (["FT", "AOT"].includes(score["strStatus"])) {
                            finalStr += "  H:" + score["strHomeTeam"] + "(" + score["intHomeScore"] + ")" + " vs. A:" + score["strAwayTeam"] + "(" + score["intAwayScore"] + ")  |"  
                        }
                    } 
                });

                if (inPlayStr === ' :::' + league + '::: In Play: ') {
                    inPlayStr = null;
                } else {
                    this.inPlayScores.NBA = inPlayStr;
                    // this.setState({inPlayScores: {"`${league}`": inPlayStr}}).bind(this);
                }
                if (finalStr === ' :::' + league + '::: Final Scores: ') {
                    finalStr = null;
                } else {
                    this.finalScores.NBA = finalStr;
                    // this.setState({finalScores: {"`${league}`": finalStr}}).bind(this);
                }
            })
            .catch(err => {
                console.log(err);
            });
        } else {
            console.log('no endpoint or leagues specified!');
        }
                console.log('do we have a this.state.finalscores?');
                console.log(this.finalScores);
    }        


    render(){
        console.log(this.state.scores);
        if (this.state.scores === null) {
            // Render loading state ...
            return(
                    <View style={globalStyles.scorestream}>
                        <Text style={globalStyles.scoreheader}>Live Scores</Text>
                        <TextTicker style={globalStyles.scoreStreamText}
                        duration={25000}
                        scrollSpeed={10000}
                        easing={Easing.linear}
                        loop
                        animationType = 'scroll'
                        bounceSpeed={0}
                        >
                        ... Loading ...
                        </TextTicker>
                    </View>
            )
        } else {
            // Render real UI ...
            return(
                <View style={globalStyles.scorestream}>
                    <Text style={globalStyles.scoreheader}>Live Scores</Text>
                    <TextTicker style={globalStyles.scoreStreamText}
                    duration={100000}
                    scrollSpeed={10000}
                    easing={Easing.linear}
                    loop
                    animationType = 'scroll'
                    bounceSpeed={0}
                    >
                    {this.state.scores}
                    </TextTicker>
                </View>
            )
        }
    }
}

控制台日志输出如下:

 LOG  finalScores are null here?
 LOG  {"NBA": null, "NFL": null, "NHL": null}
 LOG  finalScores are null here as well?
 LOG  {"NBA": null, "NFL": null, "NHL": null}

 LOG  do we have a this.state.finalscores?
 LOG  {"NBA": " :::NBA::: Final Scores:   H:Minnesota Timberwolves(116) vs. A:Phoenix Suns(125)  |  H:Oklahoma City Thunder(118) vs. A:Orlando Magic(102)  |  H:Dallas Mavericks(110) vs. A:Houston Rockets(91)  |  H:Los Angeles Lakers(121) vs. A:Philadelphia 76ers(126)  |  H:Portland Trail Blazers(96) vs. A:San Antonio Spurs(133)  |", "NFL": null, "NHL": null}

您需要使用 setState 来告诉 React 为您更新 UI 这块新状态, 异步调用之后 ( API 在这种情况下调用)已完成。

loopForFetch 方法末尾的 setState 实际上 解析 API 调用之前被调用。因此,那个时刻的 class 变量 inPlayScoresfinalScores 仍然没有被更新。您可以尝试将下面的这些代码移动到请求回调中:

// ...
.then(response => response.json())
.then(response => {
    // your logic for updating class variables...
    const strStr = <>
        <Text style={{color: 'green'}}>
            {this.inPlayScores['NBA']}
            <Text style={{color: 'red'}}>
                {this.finalScores.NBA}
            </Text>
            {this.inPlayScores['NHL']}
            <Text style={{color: 'red'}}>
                {this.finalScores['NHL']}
            </Text>
            {this.inPlayScores['NFL']}
            <Text style={{color: 'red'}}>
                {this.finalScores['NFL']}
            </Text>
        </Text>
    </>
    this.setState({ scores: strStr })
})

在这种情况下,我仍然建议在 class 变量上使用 React 状态,因为它是您希望它反映在 UI 上的那种数据。这样 UI 就可以对状态变化做出“反应”。

这里 a CodeSandbox example 用于比较这两种方法。在您调用 setState 之前,class 变量更新不会反映在 UI 上。而使用“反应状态”方法,事情有点 reactive/declarative 因为状态管理和 UI 重新渲染是 handled/triggered by setState.

这个有点off-topic。但我也建议直接渲染 strStr 而不是将其保留在 this.state.scores.