【问题标题】:Render returns nothing when using promise使用 Promise 时渲染不返回任何内容
【发布时间】:2018-09-05 18:29:30
【问题描述】:

我正在尝试实现我的渲染函数,以便它等待直到我完成从网络上抓取数据(如果 AsyncStorage 没有它)或从 AsyncStorage 获取它,但我正在努力解决承诺方面的问题它:

render() {
    let pic = {
        uri: 'https://i.imgur.com/G19Jnuj.jpg'
    };
    this.renderAttributes()
        .then((response) => {
            console.log("finished val: " + this.state.name);
            console.log("finished response: " + response);
            return (
                <View style={shared.basicView}>
                    <Image source={pic} style ={styles.circleImage}/>
                    <Text style={styles.infoText}>{this.state.name}</Text>
                    <Text style={styles.titleText}>Bio</Text>
                    <Text style={styles.infoText}>blah blah</Text>
                    <Text style={styles.titleText}>Email</Text>
                    <Text style={styles.infoText}>blah blah</Text>
                    <Text style={styles.titleText}
                          onPress={() => this.props.navigation.navigate('Repositories')}>
                        Public Repos Count</Text>
                    <Text style={styles.infoText}>3</Text>
                    <Text style={styles.titleText}
                          onPress={() => this.props.navigation.navigate('Followers')}>
                        Follower Count</Text>
                    <Text style={styles.infoText}>0</Text>
                    <Text style={styles.titleText}
                          onPress={() => this.props.navigation.navigate('Following')}>
                        Following Count</Text>
                    <Text style={styles.infoText}>0</Text>
                    <Text style={styles.titleText}>Profile Created</Text>
                    <Text style={styles.infoText}>November 2015</Text>
                </View>
            )
        })
        .catch((error) => {
            console.log("Profile creation failed: " + error);
            return(null);
        })
}

类的其余部分和 renderAttributes 函数的定义如下:

class ProfileScreen extends Component {
    state: any;

    constructor(props) {
        super(props);
        this.state = {
            name: '',
            bio: '',
            email: '',
            repoCount: '',
            followerCount: '',
            followingCount: '',
            profileCreated: ''
        };
        this.renderAttributes = this.renderAttributes.bind(this);
    }

    async renderAttributes() {
        console.log("inside ga");
        let jsonData=null;
        try {
            axios.get('https://api.github.com/users/dummy')
                .then(async (response) => {
                    console.log("Response: " + JSON.stringify(response));
                    jsonData=response;
                    console.log("jsonData: " + JSON.stringify(jsonData));

                    const nameVal = await AsyncStorage.getItem('name');
                    if(nameVal !== null) {
                        console.log("name val: " + nameVal);
                        this.setState({name: nameVal});
                    }
                    else {
                        try {
                            await AsyncStorage.setItem('name', jsonData.data.name);
                        }
                        catch (error) {
                            console.log("Set name error: " + error);
                        }

                        if(jsonData.data.name == null) {
                            this.setState({name: 'n/a'});
                        }
                        else {
                            console.log("name: " + jsonData.data.name);
                            this.setState({name: jsonData.data.name});
                        }
                    }

                    const bio = await AsyncStorage.getItem('bio');
                    if(bio !== null) {
                        this.setState({bio: bio});
                    }
                    else {
                        try {
                            await AsyncStorage.setItem('bio', jsonData.data.bio);
                        }
                        catch (error) {
                            console.log(error);
                        }
                        if(jsonData.data.bio == null) {
                            this.setState({bio: 'n/a'});
                        }
                        else {
                            this.setState({bio: jsonData.data.bio});
                        }
                    }
                    // etc for each attribute in state
                })
                .catch((error) => {
                    console.log('Error fetching: ' + error);
                });

        }
        catch (error) {
            console.log('Error creating attributes: ' + error);
        }
    }

(请原谅我所有的 console.log 语句)。对我来说,无论如何这似乎都应该返回 null 或返回 View,但我不断收到错误 "Invariant Violation: ProfileScreen(...): Nothing was returned from render. This usually means a return statement is missing. Or, to render nothing, return null."。我的 catch 语句都没有被触发。老实说,我很困惑;尽管这可能是罪魁祸首,但我注意到了两件事:1. 我的 IDE 将 this.state.name 突出显示为未解析的变量名,2. 我没有在 renderAttributes 中明确定义承诺(我是否必须?)。此外,仅基于打印语句,似乎this.renderAttributes().then(response) 中的内容在axios.get.then() 之前打印;不确定这是否是一个红鲱鱼。有什么建议吗?

【问题讨论】:

  • 我不是 React 用户,但从我上次玩它的时候开始,render 不是 async 操作,要处理 async 你会改变 state,这个然后会导致 render 方法,它来自您将更新视图的状态。
  • @Keith 嗯,我想这就是我认为我在做的事情。这绝对是一个菜鸟问题,但是为什么渲染不是异步的很重要呢?如果我强迫它等待renderAttributes(),那还不够吗?
  • I guess that's what I thought I was doing,一旦你使用 Promises、async / await / callbacks 等,你就使用 async.. ps。当你 await 某些东西时,它不会成为 sync 方法。

标签: javascript react-native promise render


【解决方案1】:

这里的问题是你试图在 render() 函数中解析一个 Promise,而 Promise 本身是异步的,你没有返回任何东西,所以隐式地返回 undefined。

渲染函数应该是纯的,没有任何副作用。

您应该将异步逻辑以获取数据放在反应生命周期挂钩之一中,例如 componentWillMount() 或 componentDidMount()

https://reactjs.org/docs/state-and-lifecycle.html

【讨论】:

  • 好的,我阅读了文档,我想确保我理解正确;我应该从render 中拨打renderAttributes 的电话,并纯粹以this.state.&lt;attributeName&gt; 作为我的元素的值返回视图。然后,在componentDidMount 中,我应该调用renderAttributes,这将触发具有更新状态值的渲染“重新评估”?
  • 确切地说,你的 renderAttributes() 中包含的“不纯”逻辑应该放在一个生命周期钩子中,比如 componentDidMount(),当承诺被解决时,你设置状态,这将触发重新渲染。
  • edit:看起来调用componentWillMount 会更好,实际上
  • 这两者之间有一个很好的比较:daveceddia.com/…
【解决方案2】:

其实as stated by docs最好的地方,就是componentDidMount

您应该在第一次运行时提供“加载屏幕”(如空字段和/或微调器),当检索数据(全部在 componentDidMount 中完成)并设置状态时,显示它

componentWillMount:

避免在此方法中引入任何副作用或订阅。 对于这些用例,请改用 componentDidMount()。

componentDidMount:

componentDidMount() 在组件被调用后立即调用 安装。需要 DOM 节点的初始化应该放在这里。如果你 需要从远程端点加载数据,这是一个很好的地方 实例化网络请求。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-04-05
    • 1970-01-01
    • 2021-01-01
    • 2019-04-14
    • 1970-01-01
    • 1970-01-01
    • 2022-08-23
    • 2018-07-09
    相关资源
    最近更新 更多