Rendering React компоненты с promises внутри метода визуализации
У меня есть компонент, который получает набор элементов как реквизит и map
их в набор компонентов, которые отображаются как дочерние элементы родительского компонента. Мы используем изображения, хранящиеся в WebSQL
как массивы байтов. Внутри функции map
я получаю идентификатор изображения из элемента и делаю асинхронный вызов DAL
, чтобы получить массив байтов для изображения. Моя проблема заключается в том, что я не могу распространять обещание в React, поскольку оно не предназначено для обработки promises в рендеринге (не так далеко, насколько я могу сказать). Я пришел из фона 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
уже был там, я не могу понять, где я могу это сделать.
Ответы
Ответ 1
render()
метод должен отображать UI из этого .props и this.state, поэтому для асинхронной загрузки данных вы можете использовать this.state
для хранения отображения imageId:imageUrl
.
Затем в вашем методе componentDidMount()
вы можете заполнить imageUrl из imageId. Тогда метод render()
должен быть чистым и простым, создавая объект this.state
Вот пример быстрого примера:
Обратите внимание, что this.state.imageUrls
заполняется асинхронно, поэтому элемент списка рендеринга будет отображаться один за другим после его URL-адреса. Вы также можете инициализировать this.state.imageUrls
всем идентификатором изображения или индексом (без URL-адресов), таким образом вы можете показать загрузчик, когда это изображение загружается.
constructor(props) {
super(props)
this.state = {
imageUrls: []
};
}
componentDidMount() {
var self = this;
this.props.items.map((item) => {
ImageStore.getImageById(item.imageId).then(image => {
var mapping = {id: item.imageId, url: image.url};
var newUrls = self.state.imageUrls.slice();
newUrls.push(mapping);
self.setState({
imageUrls: newUrls
});
})
});
}
render() {
return (<div>
this.state.imageUrls.forEach(mapping => {
<div>id: {mapping.id}, url: {mapping.url}`</div>
})
</div>
);
}
Ответ 2
Или вы можете использовать ответное обещание:
Установите пакет:
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 г.
Последняя сборка реагирования-обещания предоставляет ловушку под названием usePomise
:
import usePromise from 'react-promise';
const ExampleWithAsync = (props) => {
const {value, loading} = usePromise<string>(prom)
if (loading) return null
return <div>{value}</div>}
}
Полные документы: Реакция-обещание