React Native: как динамически визуализировать несколько изображений после извлечения из нескольких остальных API-интерфейсов - PullRequest
1 голос
/ 25 января 2020

Мне не удается отобразить несколько изображений, извлеченных из нескольких вызовов API rest в естественном режиме реакции.

Для справки API Rest я использую API rest woocommerce для получения деталей заказа. https://woocommerce.github.io/woocommerce-rest-api-docs/#retrieve -an-order

Проблема в том, что детали заказа не имеют основного изображения line_items в остальных API. Поэтому мне нужно вызвать каждый из приведенных ниже API сведений о продукте для получения изображений продуктов для каждого объекта line_item через product_id, снова вызвав API данных продукта rest.

https://woocommerce.github.io/woocommerce-rest-api-docs/#retrieve -a-product

До сих пор я написал логи c для вызова сведений о продукте для каждого line_items, но я получаю следующую ошибку с моим кодом. Как лучше всего справиться с этой ситуацией?

Warning: Cannot update during an existing state transition (such as within `render`). Render methods should be a pure function of props and state.

Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.

Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in %s.%s, the componentWillUnmount method,

Ниже приведена моя реализация:

render() {
        if (this.state.loading) {
            return (
                <View style={{ flex: 1, justifyContent: "center", alignContent: "center", padding: 20 }}>
                    <ActivityIndicator color='#96588a' size='large' />
                </View>
            )
        }

        return (
            <ScrollView style={{ flex: 1 }}>
                {this.displayOrderDataSection()}
                {this.displayProductSection()}
                {this.displayPaymentSection()}
                {this.displayShippingDetailsSection()}
                {this.displayBillingDetailsSection()}
            </ScrollView>
        );
    }

    getProductPrimaryImage = (productId) => {
        let productData = null;
        this.setState({ imageLoading: true });
        let url = `${base_url}/wp-json/wc/v3/products/${productId}?consumer_key=${c_key}&consumer_secret=${c_secret}`
        console.log(url);
        fetch(url)
            .then((response) => response.json())
            .then((responseJson) => {
                this.setState({
                    imageLoading: false,
                    error: responseJson.code || null,
                });
                productData = responseJson
            })
            .then(() => {
                return productData ?
                    ((Array.isArray(productData.images) && productData.images.length) ?
                        productData.images[0].src : null)
                    : null;

            })
            .catch((error) => {
                this.setState({
                    error,
                    imageLoading: false,
                })
            });
    }

    getLineItems = () => {
        let itemArray = [];
        orderData.line_items.forEach(item => {
            let imgSrc = this.getProductPrimaryImage(item.product_id)
            itemArray.push(
                <View key={item.id} style={{ flex: 1, flexDirection: 'row', backgroundColor: 'white' }}>
                    <View style={{ flex: 1, justifyContent: "center", alignContent: "center" }}>
                        <Image source={imgSrc}
                            style={{ height: 100, width: 100 }} resizeMode='contain' />
                    </View>
                    <View style={{ flex: 2, marginTop: 10, marginBottom: 10, justifyContent: "center" }}>
                        <View style={{ marginLeft: 10 }}>
                            <Text>{item.name}</Text>
                            <Text>SKU: {item.sku}</Text>
                            <Text>Price: {this.getCurrencySymbol()}{item.price.toFixed(2)}</Text>
                            <Text>Oty: {item.quantity}</Text>
                            <View>{this.getTMProductOptions(item.meta_data)}</View>
                        </View>
                    </View>
                </View>
            )
        })
        return itemArray;
    }

    displayProductSection = () => {
        return (
            <View style={styles.section}>
                <Text style={styles.titleText}>Product</Text>
                {this.getLineItems()}
            </View>
        )
    }

Ответы [ 2 ]

1 голос
/ 27 января 2020

Я действительно ценю tmdesigned за то, что дал мне правильное руководство. Я снова узнал концепцию реагирующих компонентов по этой ссылке .

. Поэтому я решил свою проблему, связав последующие запросы на выборку как обратный вызов в setState, который вызывается внутри componentDidMount. Ниже моя реализация

    componentDidMount() {
        this.focusListener = this.props.navigation.addListener('didFocus', () => {
            this.fetchOrderDetails()
        });
    }

    fetchOrderDetails = () => {
        const url = `${base_url}/wp-json/wc/v3/orders/${orderId}?consumer_key=${c_key}&consumer_secret=${c_secret}`;
        this.setState({ loading: true });
        fetch(url).then((response) => response.json())
            .then((responseJson) => {
                this.setState({
                    orderData: responseJson,
                    error: responseJson.code || null,
                }, this.fetchOrderStatus())
            }).catch((error) => {
                this.setState({
                    error,
                    loading: false
                })
            });
    }

    fetchOrderStatus = () => {
        const orderStatusesurl = `${base_url}/wp-json/wc/v3/reports/orders/totals?consumer_key=${c_key}&consumer_secret=${c_secret}`;
        fetch(orderStatusesurl).then(response => response.json())
            .then(responseJson => {
                let orderStatusMap = new Map();
                if (Array.isArray(responseJson) && responseJson.length > 0) {
                    if ('slug' in responseJson[0] && 'name' in responseJson[0]) {
                        responseJson.forEach(item => {
                            orderStatusMap.set(item.slug, item.name)
                        })
                    }
                }
                this.setState({
                    orderStatusOptions: [...orderStatusMap],
                    orderStatusValue: this.state.orderData.status,
                    loading: false,
                }, this.fetchOrderProductImages())
            })
    }

    fetchOrderProductImages = () => {
        this.state.orderData.line_items.forEach((item, index) => {
            this.fetchProductPrimaryImage(item.product_id, index)
        })
    }

    fetchProductPrimaryImage = (productId, index) => {
        this.setState({ imageLoading: true });
        let url = `${base_url}/wp-json/wc/v3/products/${productId}?consumer_key=${c_key}&consumer_secret=${c_secret}`
        fetch(url)
            .then((response) => response.json())
            .then(responseJson => {
                if ('images' in responseJson && Array.isArray(responseJson.images) && responseJson.images.length) {
                    if ('line_items' in this.state.orderData && Array.isArray(this.state.orderData.line_items) && this.state.orderData.line_items.length) {
                        let modifiedOrderData = this.state.orderData
                        modifiedOrderData.line_items[index].primary_image_src = responseJson.images[0].src
                        this.setState({
                            orderData: modifiedOrderData,
                            imageLoading: false,
                            error: responseJson.code || null,
                        })
                    }
                } else {
                    this.setState({
                        imageLoading: false,
                        error: responseJson.code || null,
                    });
                }
            })
            .catch((error) => {
                this.setState({
                    error,
                    imageLoading: false,
                })
            });
    }

    getLineItems = () => {
        let itemArray = [];
        this.state.orderData.line_items.forEach(item => {
            itemArray.push(
                <View key={item.id} style={{ flex: 1, flexDirection: 'row', backgroundColor: 'white' }}>
                    <View style={{ flex: 1, justifyContent: "center", alignContent: "center" }}>
                        <Image source={'primary_image_src' in item?{uri: item.primary_image_src}:null}
                            style={{ height: 100, width: 100 }} resizeMode='contain' />
                    </View>
                    <View style={{ flex: 2, marginTop: 10, marginBottom: 10, justifyContent: "center" }}>
                        <View style={{ marginLeft: 10 }}>
                            <Text>{item.name}</Text>
                            <Text>SKU: {item.sku}</Text>
                            <Text>Price: {this.getCurrencySymbol()}{item.price.toFixed(2)}</Text>
                            <Text>Oty: {item.quantity}</Text>
                            <View>{this.getTMProductOptions(item.meta_data)}</View>
                        </View>
                    </View>
                </View>
            )
        })
        return itemArray;
    }

    render() {
        if (this.state.loading) {
            return (
                <View style={{ flex: 1, justifyContent: "center", alignContent: "center", padding: 20 }}>
                    <ActivityIndicator color='#96588a' size='large' />
                </View>
            )
        }

        return (
            <ScrollView style={{ flex: 1 }}>
                {this.displayOrderDataSection()}
                {this.displayProductSection()}
                {this.displayPaymentSection()}
                {this.displayShippingDetailsSection()}
                {this.displayBillingDetailsSection()}
            </ScrollView>
        );
    }


1 голос
/ 25 января 2020

Способ думать о методе render () заключается в том, что он может запускаться неоднократно. В большинстве случаев оно перезапускается каждый раз, когда есть изменение, которое влияет на его вывод.

Как вы структурировали, ваша функция render () вызывает {this.displayProductSection()}, которая вызывает this.getLineItems(), которая вызывает this.getProductPrimaryImage(item.product_id) что делает запрос AJAX к API WordPress.

Поскольку рендеринг может (и, скорее всего, будет) выполняться многократно, это означает, что ваш запрос на изображение создается неоднократно.

Выполнение запроса AJAX равно , а не , как при отображении изображения, когда вы помещаете URL-адрес sr c в тег, и браузер загружает его один раз. HTML анализируется и запускается один раз, он запрашивает его повторно.

Лучшим шаблоном будет:

  • В состоянии отслеживать, запрашивались ли еще удаленные данные. Вы можете сделать свойство status с возможными строками init, loading, success, error.
  • В вашем componentDidMount запросите данные. Измените статус с init на загрузку.
  • Когда данные поступят, измените статус с загрузки на succcess (или ошибку, в зависимости от результата) и сохраните результат в состоянии.
  • In Ваша функция рендеринга, сделайте условие на основе этого свойства статуса. Если он загружается, покажите загрузчик. В случае успеха выведите изображение на основе состояния, которое вы сохранили выше.

Иногда вы не совсем готовы извлечь удаленные данные при монтировании компонента. Может быть, это зависит от ввода пользователя в первую очередь. В этом случае вы можете подключиться к componentDidUpdate. Проверьте ваше состояние там, но так как этот вызов функции также выполняется многократно, также проверьте статус и запрашивайте его, только если он еще не был запрошен.

В любом случае обратите внимание на разделение интересов. Ваша функция render () выполняет одну функцию - отображение. Это не отбрасывание сетевых запросов или побочных эффектов. Ваши методы жизненного цикла (или функции, которые реагируют на ввод пользователя) обрабатывают эти вещи.

...