【问题标题】:React Native elements not rerendering after state changeReact Native 元素在状态更改后不重新渲染
【发布时间】:2020-09-03 14:35:27
【问题描述】:

我已经查看此错误 2 天了,但我似乎无法弄清楚它是如何以及为什么不起作用。我的应用程序中有一个 FlatListProductComponent 元素,可以通过推送在 ProductComponent 中定义的 TouchableHighlight 组件进行买卖。
下面是我的FlatList 组件的代码和它呈现的ProductComponent

FlatList 组件:

    const ProductList: React.FC = () => {
    const energyResources = useSelector((state: RootState) => state.energyResources).asImmutable();
    const dispatch = useDispatch<RootDispatch>();
    const coins = useSelector((state: RootState) => state.coins).coins;

    const purchaseItem = (item: IEnergyResource): void => {
        if (coins > item.price) {
            dispatch.energyResources.increaseAmountOwned(dispatch.energyResources, item);
            dispatch.coins.buy(dispatch.coins, item.price);

            Alert.alert(
                'Gekocht!',
                'Je hebt zojuist de energiebron ' + item.name + ' gekocht, ' +
                '\nJe hebt nu nog €' + coins + ' Euro!'
            );
        } else {
            Alert.alert(
                'Niet gekocht',
                'Sorry, je hebt niet genoeg geld om deze energiebron te kopen.'
            );
        }
    };

    const sellItem = (item: IEnergyResource): void => {
        if (item.amountOwned > 0) {
            dispatch.energyResources.decreaseAmountOwned(dispatch.energyResources, item);
            dispatch.coins.sell(dispatch.coins, item.price);

            Alert.alert(
                'Verkocht!',
                'De energiebron ' + item.name + ' is verkocht, ' +
                'je hebt nu €' + coins + ' Euro.'
            );
        }  else {
            Alert.alert(
                'Niet verkocht',
                'Sorry, de energiebron ' + item.name + ' kon niet worden verkocht, ' +
                'je hebt er geen in je bezit.'
            );
        }
    };

    return (
        <FlatList
            style={styles.list}
            data={energyResources.toArray()}
            renderItem={
                ({ item }) => <ProductComponent
                    resource={item}
                    onPurchase={purchaseItem}
                    onSell={sellItem}
                    canSell={item.amountOwned > 0}
                />
            }
            keyExtractor={(item) => item.name}
        />
    );
}

购买和销售方法都在FlatList-组件中定义,然后传递给各个元素(我认为这比在ProductComponent 本身中定义函数并让每个渲染的项目调用状态更好)。

ProductComponent 组件:

export interface IProductProps {
    resource: IEnergyResource;
    onPurchase: Function;
    onSell: Function;
    canSell: boolean;
}

const styles = StyleSheet.create({
    buttonRight: {
        alignItems: 'center',
        alignSelf: 'flex-end',
        backgroundColor: Constants.Colors.DodgerBlue,
        marginVertical: 5,
        padding: 10,
        width: 150
    },
    image: {
        alignSelf: 'flex-start',
        height: 100,
        width: 100
    },
    listItem: {
        borderBottomColor: Constants.Colors.Blue,
        borderBottomWidth: 2,
        flex: 1,
        flexDirection: 'row',
        marginLeft: 5,
        marginBottom: 0,
        paddingBottom: 5
    },
    productInfo: {
        width: 300,
    },
    rightButtons: {
        alignItems: 'center',
        alignSelf: 'flex-end'
    },
    sell: {
        backgroundColor: Constants.Colors.Red
    },
    textLeft: {
        alignSelf: 'flex-start',
        fontSize: 20,
    }
});

const ProductComponent: React.FC<IProductProps> = (props) => {

    return (
        <View style={styles.listItem}>
            <Image source={props.resource.image.image} width={50} height={50} style={styles.image} />
            <View style={styles.productInfo}>
                <Text style={styles.textLeft}>{props.resource.name}</Text>
                <Text style={styles.textLeft}>€{props.resource.price}</Text>
                <Text style={styles.textLeft}>Energiewaarde: {props.resource.energyValue} Watt</Text>
                <Text style={styles.textLeft}>In bezit: {props.resource.amountOwned}</Text>
            </View>
            <View style={styles.rightButtons}>
                <TouchableHighlight style={styles.buttonRight} onPress={() => props.onPurchase(props.resource)}>
                    {/* eslint-disable-next-line react-native/no-inline-styles */}
                    <Text style={{ color: Constants.Colors.White, fontWeight: 'bold', fontSize: 15 }}>Kopen</Text>
                </TouchableHighlight>
                <TouchableHighlight style={[styles.buttonRight, styles.sell]} disabled={props.canSell} onPress={() => props.onSell(props.resource)}>
                    {/* eslint-disable-next-line react-native/no-inline-styles */}
                    <Text style={{ color: Constants.Colors.White, fontWeight: 'bold', fontSize: 15 }}>Verkopen</Text>
                </TouchableHighlight>
            </View>
        </View>
    );
};

这些方法存在于 IProductProps 接口中,并且调用似乎有效(我得到了定义的Alert,就好像我确实购买了产品一样)。但是,在警报框中单击确定后,指定产品的计数器(在ProductComponent 中定义为props.resource.amountOwned)仍然为0,我无法销售相同的产品。

这些是我调用的状态模型中定义的方法:

/**
 * Increases the amount of items the player owns.
 *
 * @param {List<IEnergyResource>} state - the current state of the app
 * @param {IEnergyResource} item - the item which was bought
 * @returns {List<IEnergyResource>} state
 */
function increaseAmountOwned(state: EnergyResourcesState, item: IEnergyResource): EnergyResourcesState {
    const itemIndex = state.indexOf(item);
    const newItem = {
        amountOwned: item.amountOwned++,
        ...item
    };

    return state.set(itemIndex, newItem);
}

/**
 * Decreases the amount of items the player owns.
 *
 * @param {List<IEnergyResource>} state - The current state of the app
 * @param {IEnergyResource} item - the item which was sold
 * @returns {List<IEnergyResource>} state
 */
function decreaseAmountOwned(state: EnergyResourcesState, item: IEnergyResource): EnergyResourcesState {
    const itemIndex = state.indexOf(item);
    const newItem = {
        amountOwned: item.amountOwned--,
        ...item
    } as IEnergyResource;

    return state.set(itemIndex, newItem);
}

const initialState: IEnergyResource[] =
    [
        {
            name:'Windmolen',
            price: 15,
            energyValue:20,
            environmentValue:-15,
            image:{

                maxSize:{
                    width:0.2,
                    height:0.2,
                },
                image: images.windmill,
            },
            amountOwned: 0,
            amountTotal: 30,
            amountPlaced: 0,
            location:{
                x:0.05,
                y:0
            },
        },{
            name:'Watermolen',
            price: 15,
            energyValue:15,
            environmentValue:-10,
            image:{

                maxSize:{
                    width:0.2,
                    height:0.2,
                },
                image: images.turbine
            },
            amountOwned: 0,
            amountTotal: 30,
            amountPlaced: 0,
            location:{
                x:0.05,
                y:0.3
            },
        },{
            name:'Zonnepaneel',
            price: 15,
            energyValue:10,
            environmentValue:-5,
            image:{

                maxSize:{
                    width:0.2,
                    height:0.2,
                },
                image: images.solarPanel
            },
            amountOwned: 0,
            amountTotal: 30,
            amountPlaced: 0,
            location:{
                x:0.3,
                y:0
            },
        }
    ]

const resources = {
    state: List(initialState),
    reducers: {
        place:(state: EnergyResourcesState, name: string): EnergyResourcesState =>
            state = doMutateEnergyResource(state, name, true),
        remove:(state: EnergyResourcesState, name: string): EnergyResourcesState =>
            state = doMutateEnergyResource(state, name, false),
        increaseAmountOwned: increaseAmountOwned,
        decreaseAmountOwned: decreaseAmountOwned
    },
};

我正在使用Rematch Redux framework 处理状态。

这可能是我在这里忽略的一个小细节,因此非常感谢任何朝着正确方向提供的帮助或推动。

提前谢谢你!

【问题讨论】:

  • 我的猜测是increaseAmountOwned 中的energyResources 数组正在更新但没有重新创建。如果数组引用未更改,选择器可能会进行浅层比较并放弃。可以检查您是否发布increaseAmountOwned 的代码。
  • @nipuna777 increaseAmountOwned 的代码据我所知已经存在,它是最后一个代码块中的第一个函数!
  • 知道了。我之前一定错过了。我看到您使用List 来存储状态,它使用哪个库?您是否尝试过使用数组?此外,为了调试它,我会首先设置更多断点(或警报)以查看减速器是否再次调用渲染方法。如果您可以在 stackblitz 或类似网站上制作一个最小可重现的演示,我可以帮助进一步调试。
  • 感谢@nipuna777 的帮助!我现在发现了问题(见答案)。我项目组中的其他人负责对状态管理器进行编程,他到处使用List-objects。我也不知道为什么。

标签: typescript react-native redux


【解决方案1】:

事实证明,我误解了 Rematch Reducers 与状态相关的工作方式。它们将被视为定义状态的扩展方法(在这种情况下为resources)。从 reducer 定义中可以看出,reducer 有两个参数;当前状态以及要进行的任何更改。

据我了解,我需要为用户提供当前活动视图(在本例中为 FlatList)中的状态,如下所示:

 dispatch.energyResources.increaseAmountOwned(dispatch.energyResources, item);

这原来是我的错误,因为 state 参数是由 Rematch 在调用该方法时在后台提供的。该方法需要像这样调用:

 dispatch.energyResources.increaseAmountOwned(item);

Rematch 会注意到increaseAmountOwned() 调用,并在执行前将dispatch.energyResources 中存在的当前状态注入到方法中。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-10-15
    • 2018-12-12
    • 2021-06-25
    • 1970-01-01
    • 2017-03-26
    • 2020-03-10
    • 1970-01-01
    相关资源
    最近更新 更多