在 render 方法中使用 promise 渲染 React 组件

IT技术 javascript reactjs q
2021-01-31 14:36:08

我有一个组件,它获取一组项目作为props,map并将它们放入一组组件中,这些组件被呈现为父组件的子组件。我们使用存储在WebSQL字节数组中的图像在该map函数中,我从项目中获取图像 ID,并对 进行异步调用DAL以获取图像的字节数组。我的问题是我无法将Promise传播到 React,因为它不是为了处理渲染中的Promise而设计的(无论如何我都不能说)。我来自一个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,但随后出现错误,因为我的渲染函数返回了一个Promise而不是合法的 JSX。我在想,也许我需要利用React生命周期方法之一来获取数据,但由于我需要props已经在那里,我不知道在哪里可以做到这一点。

2个回答

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

然后在您的componentDidMount()方法中,您可以imageUrlimageId. 那么render()方法应该是通过渲染this.state对象来实现的

请注意,this.state.imageUrls是异步填充的,因此渲染的图像列表项将在获取其 url 后一一出现。您还可以this.state.imageUrls使用所有图像 id 或索引(不带 url)初始化,这样您就可以在加载该图像时显示加载器。

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>
    );
}
@Wint 这一行有一个错误:var newUrls = self.state.imageUrls.slice().push(mapping)因为返回的值不是数组的浅拷贝,而是新数组的长度。参见Array.prototype.push()
2021-03-18 14:36:08
@Wint,反应很新,所以请原谅我缺乏理解,但我有几个问题:(1)这似乎是一个性能地雷。对计时器或缓存是否对以后的迭代友好以减少克隆数组的时间?(在 Ember 中,我们有运行循环要调度,不确定 React 中是否有等价的) (2) 这似乎会按照异步操作完成的顺序返回一些内容。您是否建议采取任何策略来保持秩序?
2021-03-27 14:36:08
谢谢!这就是诀窍。我在这里唯一要改变的事情(至少对于我的使用)是这样做componentWillMount而不是componentDidMount为了没有初始的“空”渲染。componentWillMount在初始渲染之前运行。
2021-03-30 14:36:08

或者你可以使用 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