Как обновить функциональный сетевой извлеченный плоский список в реагировать нативно с поддерживаемой операцией crud на разных экранах - PullRequest
0 голосов
/ 21 января 2020

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

Как обновить представление, состоящее из плоский список данных списка продуктов, получаемых из сети, при изменении данных продукта путем редактирования экрана продукта с помощью rest api. Я застрял в этой проблеме, потому что я не мог найти способ обновить этот плоский список путем редактирования экрана продукта. Буду признателен за любую помощь.

Полный код доступен на https://github.com/itsgauravjain22/wooadmin

Итак, вот резюме моей структуры кода до сих пор.

Main Navigator (App.js)
- Tab Navigator -> Product Stack Navigator, Order Stack Navigator
- - Product Stack Navigator -> Product List Screen, Single Product Screen, Edit Product Screen
- -  Order Stack Navigator -> Order List Screen, Order Details Screen

Экран со списком продуктов, содержащий плоский список, который необходимо обновить, если данные изменяются путем редактирования экрана продукта

import React, { Component } from 'react';
import { StyleSheet, Text, View, FlatList, Image, ActivityIndicator, TouchableOpacity } from 'react-native';
import * as SecureStore from 'expo-secure-store';

export default class ProductsList extends Component {

    static navigationOptions = {
        headerTitle: 'Products',
        headerStyle: {
            backgroundColor: '#96588a',
        },
        headerTintColor: '#fff',
        headerTitleStyle: {
            fontWeight: 'bold',
        },
    };

    constructor(props) {
        super(props);
        this.state = {
            loading: false,
            data: [],
            page: 1,
            seed: 1,
            error: null,
            refreshing: false,
            base_url: null,
            c_key: null,
            c_secret: null,
        };
    }

    async componentDidMount() {
        await this.getCredentials();
        this.fetchProductList();
    }

    getCredentials = async () => {
        const credentials = await SecureStore.getItemAsync('credentials');
        const credentialsJson = JSON.parse(credentials)
        this.setState({
            base_url: credentialsJson.base_url,
            c_key: credentialsJson.c_key,
            c_secret: credentialsJson.c_secret,
        })
    }

    fetchProductList = () => {
        const { base_url, c_key, c_secret, page } = this.state;
        const url = `${base_url}/wp-json/wc/v3/products?per_page=20&page=${page}&consumer_key=${c_key}&consumer_secret=${c_secret}`;
        this.setState({ loading: true });
        setTimeout(() => {
            fetch(url).then((response) => response.json())
                .then((responseJson) => {
                    this.setState({
                        data: [...this.state.data, ...responseJson],
                        error: responseJson.error || null,
                        loading: false,
                        refreshing: false
                    });
                    // this.state.data.forEach(item => console.log(`${item.sku}: ${item.stock_quantity}`))
                }).catch((error) => {
                    this.setState({
                        error,
                        loading: false,
                        refreshing: false
                    })
                });
        }, 1500);
    };

    renderListSeparator = () => {
        return (
            <View style={{
                height: 1,
                width: '100%',
                backgroundColor: '#999999'
            }} />
        )
    }

    renderListFooter = () => {
        if (!this.state.loading) return null;

        return (
            <View style={{
                paddingVertical: 20,
            }}>
                <ActivityIndicator color='#96588a' size='large' />
            </View>
        )
    }

    handleRefresh = () => {
        this.setState(
            {
                page: 1,
                refreshing: true,
                seed: this.state.seed + 1,
            },
            () => {
                this.fetchProductList();
            }
        )
    }

    handleLoadMore = () => {
        this.setState(
            {
                page: this.state.page + 1,
            },
            () => {
                this.fetchProductList();
            }
        )
    }

    render() {
        return (
            <FlatList
                data={this.state.data}
                keyExtractor={item => item.id.toString()}
                refreshing={this.state.refreshing}
                extraData={this.state}
                onRefresh={this.handleRefresh}
                onEndReached={this.handleLoadMore}
                onEndReachedThreshold={100}
                ItemSeparatorComponent={this.renderListSeparator}
                ListFooterComponent={this.renderListFooter}
                renderItem={({ item }) =>
                    <TouchableOpacity onPress={() => {
                        this.props.navigation.navigate('ProductDetails', {
                            productId: item.id,
                            productName: item.name,
                            productData: item,
                            base_url: this.state.base_url,
                            c_key: this.state.c_key,
                            c_secret: this.state.c_secret
                        });
                    }}>
                        <View style={{ flex: 1, flexDirection: 'row', backgroundColor: 'white' }}>
                            <View style={{ flex: 1, justifyContent: "center", alignContent: "center" }}>
                                <Image source={(Array.isArray(item.images) && item.images.length) ?
                                    { uri: item.images[0].src } :
                                    require('../../../assets/images/blank_product.png')}
                                    onError={(e) => { this.props.source = require('../../../assets/images/blank_product.png') }}
                                    style={{ height: 115, width: 115 }} resizeMode='contain' />
                            </View>
                            <View style={{ flex: 2, marginTop: 10, marginBottom: 10, justifyContent: "center" }}>
                                <View style={{ marginLeft: 10 }}>
                                    <Text style={styles.titleText}>{item.name}</Text>
                                    <Text>SKU: {item.sku}</Text>
                                    <Text>Price: {item.price}</Text>
                                    <Text>Stock Status:  {item.stock_status}</Text>
                                    <Text>Stock: {item.stock_quantity}</Text>
                                    <Text>Status: {item.status}</Text>
                                </View>
                            </View>
                        </View>
                    </TouchableOpacity>
                }
            />
        );
    }
}

const styles = StyleSheet.create({
    titleText: {
        fontSize: 20,
        fontWeight: 'bold',
    }
});

Экран сведений о продукте

import React, { Component } from 'react';
import { StyleSheet, Text, View, TouchableOpacity, Image, ScrollView, ActivityIndicator } from 'react-native';
import { Ionicons } from '@expo/vector-icons';
export default class ProductDetails extends Component {
    constructor(props) {
        super(props);
        this.state = { loading: false };
        productData = this.props.navigation.getParam('productData');
        base_url = this.props.navigation.getParam('base_url');
        c_key = this.props.navigation.getParam('c_key');
        c_secret = this.props.navigation.getParam('c_secret');
    }

    static navigationOptions = ({ navigation }) => {
        return {
            title: navigation.getParam('productName', 'Product'),
            headerStyle: {
                backgroundColor: '#96588a',
            },
            headerTintColor: '#fff',
            headerTitleStyle: {
                fontWeight: 'bold',
            },
        };
    };

    getProductImages() {
        let productImagesData = [];
        productData.images.forEach(item => {
            productImagesData.push(
                <Image key={`image_${item.id}`} source={(item) ? { uri: item.src } : require('../../../assets/images/blank_product.png')}
                    onError={(e) => { this.props.source = require('../../../assets/images/blank_product.png') }}
                    style={{ width: 150, height: 150 }} resizeMode='contain' />
            )
        });
        return <ScrollView horizontal={true}>{productImagesData}</ScrollView>
    }

    //Get product categories data
    getProductCategories() {
        let productCategoriesData = [];
        productData.categories.forEach(item => {
            productCategoriesData.push(
                <View key={`category_${item.id}`} style={{ flexDirection: "row", margin: 10, backgroundColor: 'white' }}>
                    <Text>{item.name ? item.name : null}</Text>
                </View>
            )
        })
        return productCategoriesData;
    }

    //Get product attributes data
    getProductAttributes() {
        let productAttributesData = [];
        productData.attributes.forEach(item => {
            let attributesOptions = [];
            item.options.forEach((option, index) =>
                attributesOptions.push(
                    <View key={`attribute_${item.id}_option${index}`} style={{ flexDirection: "row", margin: 5, backgroundColor: 'white' }}>
                        <Text>{option}</Text>
                    </View>
                )
            )
            productAttributesData.push(
                <View key={`attribute_${item.id}`} style={{ marginTop: 10 }}>
                    <Text>{item.name}</Text>
                    <ScrollView horizontal={true}>
                        {attributesOptions}
                    </ScrollView>
                </View>
            )
        }
        )
        return productAttributesData;
    }

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

        return (
            <View style={{ flex: 1 }}>
                <ScrollView style={{ flex: 1 }}>
                    <View style={{ height: 150 }}>
                        {this.getProductImages()}
                    </View>
                    <View style={styles.section}>
                        <Text style={styles.titleText}>{productData.name}</Text>
                        <Text>Sku: {productData.sku}</Text>
                        <Text>Slug: {productData.slug}</Text>
                        <Text>Total Ordered: {productData.total_sales}</Text>
                    </View>
                    <View style={styles.section}>
                </ScrollView>
                <TouchableOpacity
                    style={{
                        borderWidth: 1,
                        borderColor: 'rgba(0,0,0,0.2)',
                        alignItems: 'center',
                        justifyContent: 'center',
                        width: 60,
                        height: 60,
                        position: 'absolute',
                        bottom: 15,
                        right: 15,
                        backgroundColor: '#fff',
                        borderRadius: 100,
                    }}
                    onPress={() => {
                        this.props.navigation.navigate('EditProduct', {
                            productId: productData.id,
                            productName: productData.name,
                            productData: productData,
                            base_url: base_url,
                            c_key: c_key,
                            c_secret: c_secret
                        });
                    }}
                >
                    <Ionicons name="md-create" size={30} color="#96588a" />
                </TouchableOpacity>
            </View>
        );
    }
}

const styles = StyleSheet.create({
    section: {
        marginTop: 15,
        marginLeft: 15,
        marginRight: 15
    },
    titleText: {
        fontSize: 20,
        fontWeight: 'bold',
    },
});

Редактировать экран продукта, в котором отображаются данные о продукте изменения с помощью put rest api

import React, { Component } from 'react';
import { StyleSheet, Text, View, Switch, KeyboardAvoidingView, TouchableOpacity, ScrollView, ActivityIndicator, TextInput, Picker, Button } from 'react-native';
import DateTimePicker from '@react-native-community/datetimepicker';

export default class EditProduct extends Component {
    constructor(props) {
        super(props);
        productData = this.props.navigation.getParam('productData');
        base_url = this.props.navigation.getParam('base_url');
        c_key = this.props.navigation.getParam('c_key');
        c_secret = this.props.navigation.getParam('c_secret');
        this.state = {
            loading: false,
            error: null,
            name: productData.name,
            sku: productData.sku,
            regularPrice: productData.regular_price,
            salePrice: productData.sale_price,
            dateOnSaleFrom: productData.date_on_sale_from,
            showDateOnSaleFrom: false,
            dateOnSaleTo: productData.date_on_sale_to,
            showDateOnSaleTo: false,
            manageStock: productData.manage_stock,
            stockStatus: productData.stock_status,
            stockQuantity: productData.stock_quantity,
            weight: productData.weight,
            length: productData.dimensions.length,
            width: productData.dimensions.width,
            height: productData.dimensions.height,
            productType: productData.type,
            virtual: productData.virtual,
            downloadable: productData.downloadable,
        };
    }

    static navigationOptions = ({ navigation }) => {
        return {
            title: 'Edit Product',
            headerStyle: {
                backgroundColor: '#96588a',
            },
            headerTintColor: '#fff',
            headerTitleStyle: {
                fontWeight: 'bold',
            },
        };
    };

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

        return (
            <KeyboardAvoidingView style={{ flex: 1, flexDirection: 'column', justifyContent: 'center', }} behavior="padding" enabled>
                <ScrollView>
                    <View style={styles.section}>
                        <Text>Product Name</Text>
                        <TextInput
                            style={{ height: 40, borderColor: 'gray', borderWidth: 1 }}
                            onChangeText={this.handlleProductName}
                            value={this.state.name}
                        />
                    </View>
                    <View style={styles.section}>
                        <Text style={styles.titleText}>Pricing</Text>
                        <View style={{ flex: 1, marginLeft: 10, marginRight: 10, flexDirection: 'row' }}>
                            <View style={{ flex: 2 }}>
                                <Text>Regular Price: </Text>
                            </View>
                            <View style={{ flex: 2 }}>
                                <TextInput
                                    style={{ height: 30, borderBottomColor: 'gray', borderBottomWidth: 1 }}
                                    keyboardType='numeric'
                                    value={this.state.regularPrice}
                                    onChangeText={this.handleRegularPrice}
                                />
                            </View>
                        </View>
                        <View style={{ flex: 1, marginLeft: 10, marginRight: 10, flexDirection: 'row' }}>
                            <View style={{ flex: 2 }}>
                                <Text>Sale Price: </Text>
                            </View>
                            <View style={{ flex: 2 }}>
                                <TextInput
                                    style={{ height: 30, borderBottomColor: 'gray', borderBottomWidth: 1 }}
                                    keyboardType='numeric'
                                    value={this.state.salePrice}
                                    onChangeText={this.handleSalePrice}
                                />
                            </View>
                        </View>
                        <View style={{ flex: 1, marginLeft: 10, marginRight: 10, flexDirection: 'row' }}>
                            <View style={{
                                flex: 2
                            }}>
                                <Text>Sale Date From: </Text>
                            </View>
                            <View style={{ flex: 2 }}>
                                <TouchableOpacity onPress={this.handleShowDateOnSaleFrom}>
                                    <Text>{this.state.dateOnSaleFrom ? new Date(this.state.dateOnSaleFrom).toDateString() : 'Select Date'}</Text>
                                </TouchableOpacity>
                                {this.state.showDateOnSaleFrom && <DateTimePicker
                                    value={this.state.dateOnSaleFrom ? new Date(this.state.dateOnSaleFrom) : new Date()}
                                    mode='date'
                                    onChange={this.handleDateOnSaleFrom}
                                />}
                            </View>
                        </View>
                        <View style={{ flex: 1, marginLeft: 10, marginRight: 10, flexDirection: 'row' }}>
                            <View style={{
                                flex: 2
                            }}>
                                <Text>Sale Date To: </Text>
                            </View>
                            <View style={{ flex: 2 }}>
                                <TouchableOpacity onPress={this.handleShowDateOnSaleTo}>
                                    <Text>{this.state.dateOnSaleTo ? new Date(this.state.dateOnSaleTo).toDateString() : 'Select Date'}</Text>
                                </TouchableOpacity>
                                {this.state.showDateOnSaleTo && <DateTimePicker
                                    value={this.state.dateOnSaleTo ? new Date(this.state.dateOnSaleTo) : new Date()}
                                    mode='date'
                                    onChange={this.handleDateOnSaleTo}
                                />}
                            </View>
                        </View>
                    </View>
                    <View style={styles.section}>
                        <Text style={styles.titleText}>Inventory</Text>
                        <View style={{ flex: 1, marginLeft: 10, marginRight: 10, flexDirection: 'row' }}>
                            <View style={{ flex: 2 }}>
                                <Text>Manage Stock: </Text>
                            </View>
                            <View style={{ flex: 2 }}>
                                <Switch
                                    thumbColor={'#96588a'}
                                    trackColor={{ true: '#D5BCD0' }}
                                    value={this.state.manageStock}
                                    onValueChange={this.handleManageStock}
                                />
                            </View>
                        </View>
                        <View style={{ flex: 1, marginLeft: 10, marginRight: 10, flexDirection: 'row' }}>
                            <View style={{ flex: 2 }}>
                                <Text>Stock Status: </Text>
                            </View>
                            <View style={{ flex: 2 }}>
                                <Picker
                                    mode='dropdown'
                                    selectedValue={this.state.stock_status}
                                    onValueChange={this.handleStockStatus}
                                >
                                    <Picker.Item label="In Stock" value="instock" />
                                    <Picker.Item label="Out of Stock" value="outofstock" />
                                    <Picker.Item label="On Backorder" value="onbackorder" />
                                </Picker>
                            </View>
                        </View>
                        <View style={{ flex: 1, marginLeft: 10, marginRight: 10, flexDirection: 'row' }}>
                            <View style={{ flex: 2 }}>
                                <Text>Stock Quantity: </Text>
                            </View>
                            <View style={{ flex: 2 }}>
                                <TextInput
                                    style={{ height: 30, borderBottomColor: 'gray', borderBottomWidth: 1 }}
                                    keyboardType='numeric'
                                    value={this.state.stockQuantity ? this.state.stockQuantity.toString() : null}
                                    onChangeText={this.handleStockQuantity}
                                />
                            </View>
                        </View>
                    </View>
                    <View style={styles.section}>
                        <Text style={styles.titleText}>Shipping</Text>
                        <View style={{ flex: 1, marginLeft: 10, marginRight: 10, flexDirection: 'row' }}>
                            <View style={{ flex: 2 }}>
                                <Text>Weight: </Text>
                            </View>
                            <View style={{ flex: 2 }}>
                                <TextInput
                                    style={{ height: 30, borderBottomColor: 'gray', borderBottomWidth: 1 }}
                                    keyboardType='numeric'
                                    value={this.state.weight}
                                    onChangeText={this.handleWeight}
                                />
                            </View>
                        </View>
                        <View style={{ flex: 1, marginLeft: 10, marginRight: 10, flexDirection: 'row' }}>
                            <View style={{ flex: 2 }}>
                                <Text>Length: </Text>
                            </View>
                            <View style={{ flex: 2 }}>
                                <TextInput
                                    style={{ height: 30, borderBottomColor: 'gray', borderBottomWidth: 1 }}
                                    keyboardType='numeric'
                                    value={this.state.length}
                                    onChangeText={this.handleLength}
                                />
                            </View>
                        </View>
                        <View style={{ flex: 1, marginLeft: 10, marginRight: 10, flexDirection: 'row' }}>
                            <View style={{ flex: 2 }}>
                                <Text>Width: </Text>
                            </View>
                            <View style={{ flex: 2 }}>
                                <TextInput
                                    style={{ height: 30, borderBottomColor: 'gray', borderBottomWidth: 1 }}
                                    keyboardType='numeric'
                                    value={this.state.width}
                                    onChangeText={this.handleWidth}
                                />
                            </View>
                        </View>
                        <View style={{ flex: 1, marginLeft: 10, marginRight: 10, flexDirection: 'row' }}>
                            <View style={{ flex: 2 }}>
                                <Text>Height: </Text>
                            </View>
                            <View style={{ flex: 2 }}>
                                <TextInput
                                    style={{ height: 30, borderBottomColor: 'gray', borderBottomWidth: 1 }}
                                    keyboardType='numeric'
                                    value={this.state.height}
                                    onChangeText={this.handleHeight}
                                />
                            </View>
                        </View>
                    </View>
                        <View style={{ flex: 1, marginLeft: 10, marginRight: 10, flexDirection: 'row' }}>
                            <View style={{ flex: 2 }}>
                                <Text>Virtual: </Text>
                            </View>
                            <View style={{ flex: 2 }}>
                                <Switch
                                    thumbColor={'#96588a'}
                                    trackColor={{ true: '#D5BCD0' }}
                                    value={this.state.virtual}
                                    onValueChange={this.handleVirtual}
                                />
                            </View>
                        </View>
                        <View style={{ flex: 1, marginLeft: 10, marginRight: 10, flexDirection: 'row' }}>
                            <View style={{ flex: 2 }}>
                                <Text>Downloadable: </Text>
                            </View>
                            <View style={{ flex: 2 }}>
                                <Switch
                                    thumbColor={'#96588a'}
                                    trackColor={{ true: '#D5BCD0' }}
                                    value={this.state.downloadable}
                                    onValueChange={this.handleDownloadable}
                                />
                            </View>
                        </View>
                    </View>
                </ScrollView>
                <Button
                    title="Submit"
                    color="#96588a"
                    onPress={this.handleSubmit}
                />
            </KeyboardAvoidingView>
        );
    }



    handleSubmit = () => {
        let updatedProductObject = {
            "name": this.state.name,
            "sku": this.state.sku,
            "regular_price": this.state.regularPrice,
            "sale_price": this.state.salePrice,
            "date_on_sale_from": this.state.dateOnSaleFrom,
            "date_on_sale_to": this.state.dateOnSaleTo,
            "manage_stock": this.state.manageStock,
            "stock_status": this.state.stockStatus,
            "stock_quantity": this.state.stockQuantity,
            "dimensions": { "weight": this.state.weight, "length": this.state.length, "height": this.state.height },
            "type": this.state.productType,
            "virtual": this.state.virtual,
            "downloadable": this.state.downloadable,
        };

        const id = productData.id;
        const url = `${base_url}/wp-json/wc/v3/products/${id}?consumer_key=${c_key}&consumer_secret=${c_secret}`;
        this.setState({ loading: true });
        fetch(url, {
            method: 'PUT',
            body: JSON.stringify(updatedProductObject),
        }).then((response) => response.json())
            .then((responseJson) => {
                this.setState({
                    error: responseJson.code || null,
                    loading: false,
                });
            }).catch((error) => {
                this.setState({
                    error,
                    loading: false,
                })
            });
    }
}

const styles = StyleSheet.create({
    section: {
        marginTop: 20,
        marginLeft: 20,
        marginRight: 20,
    },
    titleText: {
        fontSize: 20,
        fontWeight: 'bold',
        marginBottom: 10,
    },
});
...