Подтвердить что ты не робот

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 уже был там, я не могу понять, где я могу это сделать.

4b9b3361

Ответ 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>}
}

Полные документы: Реакция-обещание