【问题标题】:My row item updates itself automatically into the deleted item how to fix this?我的行项目自动更新为已删除的项目如何解决这个问题?
【发布时间】:2019-05-10 15:50:04
【问题描述】:

我是原生反应新手,我面临的问题是,当我 从平面列表中删除一个项目,它可以很好地删除,但也可以 更新它下面的项目。它将它更新为之前的项目 删除。我做错了什么?

CartScreen.js

这是我的购物车屏幕代码

import React, { Component } from 'react';
import { View, Text, Picker, FlatList } from 'react-native';
import HeaderComp from '../components/HeaderComp'
import { Container, Content } from 'native-base';
import colors from '../assets/Colors';
import styles from '../assets/Styles';
import ButtonComp from '../components/ButtonComp';
import IconComp from '../components/IconComp'
import RowCartComp from '../components/RowCartComp';

class CartPage extends Component {
    constructor(props) {
        super(props);
        this.state = {
            cartList: [
                { name: 'Sub Item 1', image: require('../images/1.jpeg'), price: 100, remainingQty: 1 },
                { name: 'Sub Item 2', image: require('../images/2.jpeg'), price: 200, remainingQty: 2 },
                { name: 'Sub Item 3', image: require('../images/3.jpeg'), price: 300, remainingQty: 3 },
                { name: 'Sub Item 4', image: require('../images/4.jpeg'), price: 400, remainingQty: 4 },
                { name: 'Sub Item 5', image: require('../images/5.jpeg'), price: 500, remainingQty: 5 },
                { name: 'Sub Item 6', image: require('../images/6.jpeg'), price: 600, remainingQty: 6 },
                { name: 'Sub Item 7', image: require('../images/7.jpeg'), price: 700, remainingQty: 7 },
                { name: 'Sub Item 8', image: require('../images/8.jpeg'), price: 800, remainingQty: 8 },
                { name: 'Sub Item 9', image: require('../images/9.jpeg'), price: 900, remainingQty: 9 },
                { name: 'Sub Item 10', image: require('../images/10.jpeg'), price: 1000, remainingQty: 10 },
            ],
            grandTotal: 0
        }
    }

    componentWillMount() {
        let total = 0;
        for (let i = 0; i < this.state.cartList.length; i++) {
            total = total + this.state.cartList[i].price;
        }
        this.setState({ grandTotal: total })
    }

    updateGrandTotal = (value, op) => {
        if (op === 'add') {
            this.setState({ grandTotal: this.state.grandTotal + value });
        }
        else if (op === 'sub') {
            this.setState({ grandTotal: this.state.grandTotal - value })
        }
    }

    deleteItem = (name) => {
        this.setState(prevState => {
            return {
                cartList: prevState.cartList.filter(cartItem => {
                    return cartItem.name !== name;
                })
            }
        })
    }

    render() {
        return (
            <Container>
                <HeaderComp
                    headerTitle="CART"
                    showBackArrow={true}
                    showIcons={false}
                    backClick={
                        () => this.props.navigation.goBack()
                    } />
                <Content>
                    <View style={styles.cartPickerStyle}>
                        <View style={{ flex: 0.1 }}>
                            <IconComp
                                name='location_icon'
                                color={colors.colorBlack}
                                size={30}
                            />
                        </View>
                        <View style={{ flex: 0.9 }}>
                            <Picker
                                selectedValue={this.state.language}
                                style={{ height: 20 }}
                                onValueChange={(itemValue, itemIndex) =>
                                    this.setState({ language: itemValue })
                                }>
                                <Picker.Item label="Address A" value="A" />
                                <Picker.Item label="Address B" value="B" />
                                <Picker.Item label="Address C" value="C" />
                            </Picker>
                        </View>
                    </View>

                    <FlatList
                        data={this.state.cartList}
                        renderItem={({ item }) =>
                            <RowCartComp
                                itemName={item.name.toUpperCase()}
                                itemImage={item.image}
                                itemPrice={item.price}
                                itemRemainingQty={item.remainingQty}
                                deleteItem={() => this.deleteItem(item.name)}
                                updateGrandTotal={this.updateGrandTotal}
                            />
                        }
                    />

                    <View style={{ flexDirection: 'row', alignItems: 'center', justifyContent: 'center' }}>
                        <View style={styles.cartSeparatorStyle} />
                        <Text style={styles.cashOnDeliveryTextStyle}>
                            {" Cash on delivery "}
                        </Text>
                        <View style={styles.cartSeparatorStyle} />
                    </View>

                    <View style={styles.cartGrandTotalViewStyle}>
                        <View style={{ flex: 0.6 }}>
                            <Text style={styles.cartTextStyle}>
                                {"Grand Total"}
                            </Text>
                            <Text style={styles.cartTextStyle}>
                                {"Delivery charges"}
                            </Text>
                        </View>
                        <View style={{ flex: 0.4, alignItems: 'flex-end' }}>
                            <Text style={styles.cartTextStyle}>
                                {this.state.grandTotal}
                            </Text>
                            <Text style={styles.cartTextStyle}>
                                {"+ 30"}
                            </Text>
                            <View style={{ height: 1, borderColor: colors.colorWhite, borderWidth: 1, width: '70%' }} />
                            <Text style={styles.cartTextStyle}>
                                {this.state.grandTotal + 30}
                            </Text>
                        </View>
                    </View>

                    <ButtonComp
                        buttonText={'Place order'}
                        buttonStyle={styles.cartButtonStyle}
                        textStyle={styles.cartButtonTextStyle} />

                </Content>
            </Container>
        );
    }
}

export default CartPage;

RowCartComp.js

这是我在购物车屏幕中使用的列表的行项目

import React, { Component } from 'react';
import { View, Image, Text, Alert, } from 'react-native';
import IconComp from '../components/IconComp'
import colors from '../assets/Colors'

class RowCartComp extends Component {
    constructor(props) {
        super(props);
        this.state = {
            qtyString: 1,
            priceString: this.props.itemPrice,
            remainingQty: this.props.itemRemainingQty
        }
    }

    increaseQty = () => {
        if (this.state.qtyString < this.props.itemRemainingQty) {
            this.setState({
                qtyString: this.state.qtyString + 1,
                priceString: this.state.priceString + this.props.itemPrice,
                remainingQty: this.state.remainingQty - 1
            })
            this.props.updateGrandTotal(this.props.itemPrice, 'add')
        }
    }

    decreaseQty = () => {
        if (this.state.qtyString != 1) {
            this.setState({
                qtyString: this.state.qtyString - 1,
                priceString: this.state.priceString - this.props.itemPrice,
                remainingQty: this.state.remainingQty + 1

            })
            this.props.updateGrandTotal(this.props.itemPrice, 'sub')
        }
        else {
            Alert.alert(
                'REMOVE ITEM?',
                'Are you sure you want to remove ' + `${this.props.itemName.toLowerCase()}` + '?',
                [
                    { text: 'No' },
                    { text: 'Yes', onPress: this.combinedFunction },
                ],
                { cancelable: true },
            );
        }
    }

    combinedFunction = () => {
        this.props.deleteItem()
        this.props.updateGrandTotal(this.props.itemPrice, 'sub')
    }

    render() {
        return (
            <View style={{ margin: 10, borderColor: colors.colorBlack, borderWidth: 1 }}>
                <View style={{ flexDirection: 'row', margin: 10 }}>
                    <View>
                        <Image
                            style={{ height: 70, width: 70, borderRadius: 6, }}
                            // source={{ uri: imageURL }}
                            source={this.props.itemImage}
                        />
                    </View>
                    <View style={{ flex: 0.6, flexWrap: 'wrap', justifyContent: 'center', marginLeft: 5 }}>
                        <Text style={{ fontSize: 20, fontWeight: 'bold', color: colors.colorBlack, marginBottom: 5 }}>
                            {this.props.itemName}
                        </Text>
                    </View>

                    <View style={{ flex: 0.4, alignItems: 'flex-end' }}>
                        <View style={{ flexDirection: 'row', alignItems: 'center' }}>
                            <IconComp
                                name='minus_icon'
                                color={colors.colorBlack}
                                size={20}
                                onPress={this.decreaseQty}
                            />
                            <Text style={{ fontSize: 20, fontWeight: 'bold', color: colors.colorBlack, marginLeft: 10, marginRight: 10 }}>
                                {this.state.qtyString}
                            </Text>
                            <IconComp
                                name='plus_icon'
                                color={colors.colorBlack}
                                size={20}
                                onPress={this.increaseQty}
                            />
                        </View>

                    </View>
                </View>
                <View style={{ flexDirection: 'row', position: 'absolute', bottom: 0, right: 5, alignItems: 'center' }}>
                    <Text style={{ fontSize: 14, fontWeight: 'bold', color: colors.colorPrimaryDark, marginRight: 15 }}>
                        {"Qty Left: " + (this.state.remainingQty - 1) }
                    </Text>
                    <Text style={{ fontSize: 20, fontWeight: 'bold', color: colors.colorBlack }}>
                        {"RS: " + `${this.state.priceString}`}
                    </Text>
                </View>
            </View>
        );
    }
}

export default RowCartComp;

Styles.js

这是我为购物车页面创建样式的样式文件。 从'react-native'导入{ StyleSheet}; 从'./Colors'导入颜色

export default StyleSheet.create({
cartButtonStyle: {
        margin: 10,
        padding: 10,
        backgroundColor: colors.colorPrimaryDark,
        borderRadius: 26,
        justifyContent: 'center',
        alignItems: 'center',
        elevation: 4, // Android
        shadowColor: 'gray', // IOS
        shadowOffset: { height: 1, width: 1 }, // IOS
        shadowOpacity: 1, // IOS
        shadowRadius: 1, //IOS
    },
    cartButtonTextStyle: {
        fontSize: 24,
        fontWeight: 'bold',
        color: colors.colorBlack,
    },
    cartTextStyle: {
        fontSize: 20,
        color: colors.colorWhite,
        fontWeight: 'bold',
        margin: 5,
    },
    cashOnDeliveryTextStyle: {
        fontSize: 18,
        color: colors.colorBlack,
    },
    cartPickerStyle: {
        margin: 10,
        padding: 10,
        borderRadius: 20,
        borderWidth: 1,
        borderColor: colors.colorBlack,
        flexDirection: 'row',
        alignItems: 'center'
    },
    cartSeparatorStyle: {
        width: '30%',
        height: 1,
        borderStyle: 'dotted',
        borderColor: colors.colorBlack,
        borderWidth: 1,
        borderRadius: 1
    },
    cartGrandTotalViewStyle: {
        margin: 10,
        backgroundColor: colors.colorPrimaryDark,
        flexDirection: 'row',
        padding: 5,
        paddingTop: 10,
        paddingBottom: 10
    },
});

我使用的图标是使用 icomoon 应用程序导入的,我为它创建了一个自定义组件。 我使用的按钮还为它创建了一个自定义组件。

这些是我面临的问题的屏幕截图。

当子项 3 被删除时,子项 4 具有其剩余数量和子项 3 价格等属性。

【问题讨论】:

    标签: react-native


    【解决方案1】:

    关键是使用使您的cartItem 具有key 属性,或者在FlatList 上设置keyextractor 函数。通过这样做,反应“知道”哪个项目被删除并且可以更新正确的RowCartComp。如果您忘记拥有key 属性(或没有keyExtractor),则react 仅具有更新项目的索引,因此可能会重复使用数组的错误组件。

    查看FlatList 文档以获取更多信息。


    然而,您发布的代码实际上包含某种 React 反模式:您将 props 复制到您的子组件 RowCartComp 的状态中。这使得在RowCartComp 中管理更新和差异变得更加困难。最好将增加和减少功能提升到父容器中。这样一来,所有操作逻辑都驻留在CartScreen 中,RowCartComp 可以只是一个功能组件,并且不会出现任何不同步的情况。

    我将 RowCartComp 变成了一个函数组件,它只接受一些 props 并且没有内部状态。它接受来自父级的所有数据和处理函数

    RowCartComp.js

    import React, { Component } from "react";
    import { View, Image, Text, Alert } from "react-native";
    import IconComp from "../components/IconComp";
    import colors from "../assets/Colors";
    
    // this is now a functional component receiving
    // 1. the entire cart item including qty property
    // 2. handler to delete
    // 3. handlers to increase/decrease the quantity
    const RowCartComp = ({ item, deleteItem, increaseQty, decreaseQty }) => {
      return (
        <View
          style={{ margin: 10, borderColor: colors.colorBlack, borderWidth: 1 }}
        >
          <View style={{ flexDirection: "row", margin: 10 }}>
            <View>
              <Image
                style={{ height: 70, width: 70, borderRadius: 6 }}
                source={itemImage}
              />
            </View>
            <View
              style={{
                flex: 0.6,
                flexWrap: "wrap",
                justifyContent: "center",
                marginLeft: 5
              }}
            >
              <Text
                style={{
                  fontSize: 20,
                  fontWeight: "bold",
                  color: colors.colorBlack,
                  marginBottom: 5
                }}
              >
                {item.name}
              </Text>
            </View>
    
            <View style={{ flex: 0.4, alignItems: "flex-end" }}>
              <View style={{ flexDirection: "row", alignItems: "center" }}>
                <IconComp
                  name="minus_icon"
                  color={colors.colorBlack}
                  size={20}
                  onPress={decreaseQty}
                />
                <Text
                  style={{
                    fontSize: 20,
                    fontWeight: "bold",
                    color: colors.colorBlack,
                    marginLeft: 10,
                    marginRight: 10
                  }}
                >
                  {item.qty.toString()}
                </Text>
                <IconComp
                  name="plus_icon"
                  color={colors.colorBlack}
                  size={20}
                  onPress={increaseQty}
                />
              </View>
            </View>
          </View>
          <View
            style={{
              flexDirection: "row",
              position: "absolute",
              bottom: 0,
              right: 5,
              alignItems: "center"
            }}
          >
            <Text
              style={{
                fontSize: 14,
                fontWeight: "bold",
                color: colors.colorPrimaryDark,
                marginRight: 15
              }}
            >
              {"Qty Left: " + (item.remainingQty - item.qty)}
            </Text>
            <Text
              style={{ fontSize: 20, fontWeight: "bold", color: colors.colorBlack }}
            >
              {"RS: " + `${item.price}`}
            </Text>
          </View>
        </View>
      );
    };
    
    export default RowCartComp;
    

    另外我将 CartPage.js 组件调整为

    1. 计算每次渲染的总和,这是当前购物车列表的结果
    2. 购物车商品都有数量和 id 属性,因此平面列表可以工作
    3. 有一个可以增加或减少购物车物品数量的处理程序。然后将其传递给各个项目

    CartPage.js

    import React, { Component } from 'react';
    import { View, Text, Picker, FlatList } from 'react-native';
    import HeaderComp from '../components/HeaderComp'
    import { Container, Content } from 'native-base';
    import colors from '../assets/Colors';
    import styles from '../assets/Styles';
    import ButtonComp from '../components/ButtonComp';
    import IconComp from '../components/IconComp'
    import RowCartComp from '../components/RowCartComp';
    
    class CartPage extends Component {
        constructor(props) {
            super(props);
            this.state = {
                cartList: [
                    { id: 1, name: 'Sub Item 1', image: require('../images/1.jpeg'), price: 100, remainingQty: 1, qty: 1 },
                    { id: 2, name: 'Sub Item 2', image: require('../images/2.jpeg'), price: 200, remainingQty: 2, qty: 1 },
                    { id: 3, name: 'Sub Item 3', image: require('../images/3.jpeg'), price: 300, remainingQty: 3, qty: 1 },
                    { id: 4, name: 'Sub Item 4', image: require('../images/4.jpeg'), price: 400, remainingQty: 4, qty: 1 },
                    { id: 5, name: 'Sub Item 5', image: require('../images/5.jpeg'), price: 500, remainingQty: 5, qty: 1 },
                    { id: 6, name: 'Sub Item 6', image: require('../images/6.jpeg'), price: 600, remainingQty: 6, qty: 1 },
                    { id: 7, name: 'Sub Item 7', image: require('../images/7.jpeg'), price: 700, remainingQty: 7, qty: 1 },
                    { id: 8, name: 'Sub Item 8', image: require('../images/8.jpeg'), price: 800, remainingQty: 8, qty: 1 },
                    { id: 9, name: 'Sub Item 9', image: require('../images/9.jpeg'), price: 900, remainingQty: 9, qty: 1 },
                    { id: 10, name: 'Sub Item 10', image: require('../images/10.jpeg'), price: 1000, remainingQty: 10, qty: 1 },
                ],
            }
        }
    
        // we can just calculate this every render
        calculateGrandTotal () => {
          let total = 0;
    
          this.state.cartList.forEach(item => {
            const itemTotal  =item.qty * item.price;
            total += itemTotal;
          });
    
          return total;
        };
    
    
        adjustQuantity = (id, diff) => {
          const index = this.state.cartList.findIndex(ci => ci.id === id);
    
          const item = this.state.cartList[index];
    
          const newItem = {
            ...this.state.cartList[index],
            qty: item.qty + diff
          };
    
          // don't go above remaining quantity
          if (newItem.qty > newItem.remainingQty) {
            return;
          }
    
          // don't go below 0
          if (newItem.qty < 0) {
            return;
          }
    
          // copy list and insert new item with adjusted quantity
          const newCartList = [...this.state.cartList];
          newCartList.splice(index, 1, newItem)
    
          this.setState({
            cartList: newCartList,
          })
        };
    
        deleteItem = (id) => {
            this.setState(prevState => {
                return {
                    cartList: prevState.cartList.filter(cartItem => {
                        return cartItem.id !== id;
                    })
                }
            })
        }
    
        render() {
          // grand total is calculated on every render, as it is
          // a consequence of the current cartList
          const grandTotal = this.calculateGrandTotal();
            return (
                <Container>
                    <HeaderComp
                        headerTitle="CART"
                        showBackArrow={true}
                        showIcons={false}
                        backClick={
                            () => this.props.navigation.goBack()
                        } />
                    <Content>
                        <View style={styles.cartPickerStyle}>
                            <View style={{ flex: 0.1 }}>
                                <IconComp
                                    name='location_icon'
                                    color={colors.colorBlack}
                                    size={30}
                                />
                            </View>
                            <View style={{ flex: 0.9 }}>
                                <Picker
                                    selectedValue={this.state.language}
                                    style={{ height: 20 }}
                                    onValueChange={(itemValue, itemIndex) =>
                                        this.setState({ language: itemValue })
                                    }>
                                    <Picker.Item label="Address A" value="A" />
                                    <Picker.Item label="Address B" value="B" />
                                    <Picker.Item label="Address C" value="C" />
                                </Picker>
                            </View>
                        </View>
    
                        <FlatList
                            data={this.state.cartList}
                            keyExtractor={item => `cart_${item.id}`}
                            renderItem={({ item }) =>
                                <RowCartComp
                                  item={item}
                                  deleteItem={() => this.deleteItem(item.id)}
                                  increaseQty={() => this.adjustQuantity(item.id, +1)}
                                  decreaseQty={() => this.adjustQuantity(item.id, -1)}
                                />
                            }
                        />
    
                        <View style={{ flexDirection: 'row', alignItems: 'center', justifyContent: 'center' }}>
                            <View style={styles.cartSeparatorStyle} />
                            <Text style={styles.cashOnDeliveryTextStyle}>
                                {" Cash on delivery "}
                            </Text>
                            <View style={styles.cartSeparatorStyle} />
                        </View>
    
                        <View style={styles.cartGrandTotalViewStyle}>
                            <View style={{ flex: 0.6 }}>
                                <Text style={styles.cartTextStyle}>
                                    {"Grand Total"}
                                </Text>
                                <Text style={styles.cartTextStyle}>
                                    {"Delivery charges"}
                                </Text>
                            </View>
                            <View style={{ flex: 0.4, alignItems: 'flex-end' }}>
                                <Text style={styles.cartTextStyle}>
                                    {grandTotal}
                                </Text>
                                <Text style={styles.cartTextStyle}>
                                    {"+ 30"}
                                </Text>
                                <View style={{ height: 1, borderColor: colors.colorWhite, borderWidth: 1, width: '70%' }} />
                                <Text style={styles.cartTextStyle}>
                                    {grandTotal + 30}
                                </Text>
                            </View>
                        </View>
    
                        <ButtonComp
                            buttonText={'Place order'}
                            buttonStyle={styles.cartButtonStyle}
                            textStyle={styles.cartButtonTextStyle} />
    
                    </Content>
                </Container>
            );
        }
    }
    
    export default CartPage;
    

    希望这会有所帮助。

    【讨论】:

    • 如果我将我的功能带入父屏幕,包含数量文本的孩子如何在单击图标时增加和减少?你能告诉我怎么做吗? @托马斯
    • 将方法作为 props 传递,并在 click 事件中访问传递下来的方法,就像使用 react 一样。
    • 非常感谢你,它帮助了我很多,你让我免于大量搜索,还向我展示了处理方法并将它们作为道具发送的方式
    猜你喜欢
    • 1970-01-01
    • 2011-11-07
    • 2022-11-14
    • 2021-12-25
    • 2020-09-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-06
    相关资源
    最近更新 更多