RenderingReact 组件的渲染方法中包含承诺

我有一个组件,它获取一个项目集合作为道具,并且 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;
});
}

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

109153 次浏览

render() method should render UI from this.props and this.state, so to asynchronously load data, you can use this.state to store imageId: imageUrl mapping.

Then in your componentDidMount() method, you can populate imageUrl from imageId. Then the render() method should be pure and simple by rendering the this.state object

Note that the this.state.imageUrls is 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.imageUrls with all image id or index (without urls), this way you can show a loader when that image is being loaded.

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

Or you can use 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

The last build of react-promise provide a hook called 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