在render方法中使用承诺来渲染React组件


74

我有一个组件,该组件获取作为道具的项目map集合,并将其作为呈现为父组件子项的组件集合。我们使用存储WebSQL为字节数组的图像。在map函数中,我从项目中获取图像ID,并异步调用,DAL以获取图像的字节数组。我的问题是我无法将诺言传播到React中,因为它不是设计来处理渲染中的诺言的(无论如何我还是不能说的)。我来自C#背景,所以我猜我在寻找类似await关键字的内容来重新同步分支代码。

map函数看起来像这样(简化):

var items = this.props.items.map(function (item) {
        var imageSrc = Utils.getImageUrlById(item.get('ImageId')); // <-- this contains an async call
        return (
            <MenuItem text={item.get('ItemTitle')}
                      imageUrl={imageSrc} />
       );
    });

getImageUrlById方法如下所示:

getImageUrlById(imageId) {
    return ImageStore.getImageById(imageId).then(function (imageObject) { //<-- getImageById returns a promise
       var completeUrl = getLocalImageUrl(imageObject.StandardConImage);
       return completeUrl;
    });
}

这是行不通的,但是我不知道我需要修改什么才能使它工作。我尝试向链中添加另一个Promise,但是由于我的render函数返回一个Promise而不是合法的JSX,因此出现错误。我当时在想,也许我需要利用一种React生命周期方法来获取数据,但是由于我需要props已经存在该方法,所以我不知道该在哪里进行操作。

Answers:


89

render()方法应该从this.props和渲染UI this.state,因此要异步加载数据,可以this.state用来存储imageId: imageUrl映射。

然后,在您的componentDidMount()方法中,您可以imageUrl从填充imageId。然后,render()通过渲染this.state对象, 该方法应该是纯粹而简单的

请注意,this.state.imageUrls异步填充了,因此渲染的图像列表项在获取其网址后将一一显示。您还可以this.state.imageUrls使用所有图片ID或索引(不包含网址)初始化,这样您可以在加载该图片时显示一个加载器。

constructor(props) {
    super(props)
    this.state = {
        imageUrls: []
    };
}

componentDidMount() {
    this.props.items.map((item) => {
        ImageStore.getImageById(item.imageId).then(image => {
            const mapping = {id: item.imageId, url: image.url};
            const newUrls = this.state.imageUrls.slice();
            newUrls.push(mapping);

            this.setState({ imageUrls: newUrls });
        })
    });
}

render() {
    return (
      <div>
        {this.state.imageUrls.map(mapping => (
            <div>id: {mapping.id}, url: {mapping.url}</div>
        ))}
      </div>
    );
}

5
谢谢!做到了。我唯一要更改的地方(至少供我使用)是这样做,componentWillMount而不是componentDidMount为了没有初始的“空”渲染。componentWillMount在初始渲染之前运行。
Elad Lachmi 2015年

2
@Wint这行有一个错误:var newUrls = self.state.imageUrls.slice().push(mapping)因为返回的值不是数组的浅表副本,而是新数组的长度。参见Array.prototype.push()
jherax

1
@Wint,反应非常新,因此请原谅我缺乏理解,但是我有几个问题:(1)这似乎是个表演地雷。对计时器或缓存进行后续的迭代以减少克隆克隆的时间是否反应友好?(在Ember中,我们要安排运行循环,不确定React中是否有等效项)(2)似乎它将以异步操作完成的顺序返回某些内容。您是否建议任何保留订单的策略?
托马斯

29

或者您可以使用react-promise:

安装软件包:

npm i react-promise

您的代码将如下所示:

import Async from 'react-promise'

var items = this.props.items.map(function (item) {
    var imageSrc = Utils.getImageUrlById(item.get('ImageId')); // <-- this contains an async call
    return (
        <Async promise={imageSrc} then={(val) => <MenuItem text={item.get('ItemTitle')} imageUrl={val}/>} />    
    );
});

编辑:2019年10月

react-promise的最后一个构建提供了一个名为usePromise:的钩子:

import usePromise from 'react-promise';

const ExampleWithAsync = (props) => {
  const {value, loading} = usePromise<string>(prom)
  if (loading) return null
  return <div>{value}</div>}
}

完整文档:react-promise

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.