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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/33242378/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 14:50:04  来源:igfitidea点击:

Rendering React components with promises inside the render method

javascriptreactjsq

提问by Elad Lachmi

I have a component which gets a collection of items as props and maps them to a collection of components which are rendered as children of a parent component. We use images stored in WebSQLas byte arrays. Within the mapfunction I get an image Id from the item and make an async call to the DALin order to get the byte array for the image. My problem is that I cannot propagate the promise in to React, since it was not designed to deal with promises in rendering (not as far as I can tell anyway). I come from a C#background, so I guess I'm looking for something like the awaitkeyword for resync-ing branched code.

我有一个组件,它获取一组项目作为道具,map并将它们放入一组组件中,这些组件被呈现为父组件的子组件。我们使用存储WebSQL为字节数组的图像。在该map函数中,我从项目中获取图像 Id 并对 进行异步调用DAL以获取图像的字节数组。我的问题是我无法将承诺传播到 React,因为它不是为了处理渲染中的承诺而设计的(无论如何我都不能说)。我来自一个C#背景,所以我想我正在寻找类似await用于重新同步分支代码的关键字的东西。

The mapfunction looks something like this (simplified):

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} />
       );
    });

and the getImageUrlByIdmethod looks like this:

getImageUrlById方法如下所示:

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

This doesn't work, but I don't know what I need to modify to make this work. I tried adding another promise to the chain, but then I get an error because my render function return a promise instead of legal JSX. I was thinking that maybe I need to leverage one of the Reactlife-cycle methods to fetch the data, but since I need the propsto already be there, I can't figure out where I can do this.

这不起作用,但我不知道我需要修改什么才能使它起作用。我尝试向链中添加另一个承诺,但随后出现错误,因为我的渲染函数返回一个承诺而不是合法的 JSX。我在想,也许我需要利用React生命周期方法之一来获取数据,但由于我需要props已经在那里,我不知道在哪里可以做到这一点。

回答by Wint

render()method should render UI from this.propsand this.state, so to asynchronously load data, you can use this.stateto store imageId: imageUrlmapping.

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

Then in your componentDidMount()method, you can populate imageUrlfrom imageId. Then the render()method should be pure and simple by rendering the this.stateobject

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

Note that the this.state.imageUrlsis populated asynchronously, so the rendered image list item will appear one by one after its url is fetched. You can also initialize the this.state.imageUrlswith all image id or index (without urls), this way you can show a loader when that image is being loaded.

请注意,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>
    );
}

回答by ZEE

Or you can use react-promise :

或者你可以使用 react-promise :

Install the package :

安装软件包:

npm i react-promise

And your code will look something like so :

你的代码看起来像这样:

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}/>} />    
    );
});

Edit: Oct 2019

编辑:2019 年 10 月

The last build of react-promise provide a hook called usePromise:

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>}
}

Full docs: react-promise

完整文档:react-promise