简体   繁体   English

React-native 应用到许多重新渲染 & function 继续在 state 上运行

[英]React-native app to many re-renders & function keeps running on state change

I get an error for to many re-renders.我收到许多重新渲染的错误。 It seems that the compareDate function gets run every time a state updates.似乎每次 state 更新时,compareDate function 都会运行。 The compareDate only has to be run when the app opens to check if it's a new day since last login so that it can reset the program by doing addWater(-dailyGoal).只有在应用程序打开时才需要运行 compareDate 以检查自上次登录以来是否是新的一天,以便它可以通过执行 addWater(-dailyGoal) 来重置程序。 Does anyone know how I can make it only run once on opening the app?有谁知道我怎样才能让它在打开应用程序时只运行一次?

import { StatusBar } from 'expo-status-bar';
import React, { useState } from 'react';
import {
    StyleSheet,
    Text,
    View,
    SafeAreaView,
    Alert,
    TouchableOpacity,
    Keyboard,
    TextInput,
} from 'react-native';
import AsyncStorage from '@react-native-community/async-storage';
import { FontAwesomeIcon } from '@fortawesome/react-native-fontawesome';
import { faTint, faCog, faTimes } from '@fortawesome/free-solid-svg-icons';

export default function App() {
    // set vars
    const [waterDrank, setWaterDrank] = useState(0);
    const [menuOpen, setmenuOpen] = useState(false);
    const [dailyGoal, setDailyGoal] = useState(3700);
    const [bottle, setBottle] = useState(250);
    const [dailyGoalSettings, onChangeDailyGoalSettings] = useState(
        `${dailyGoal}`
    );
    const [bottleSettings, onChangeBottleSettings] = useState(`${bottle}`);

    // Async storage
    const storeWater = async (value) => {
        try {
            await AsyncStorage.setItem('storedWater', value);
        } catch (e) {
            // saving error
        }
    };
    const getStoredWater = async () => {
        try {
            const value = await AsyncStorage.getItem('storedWater');
            if (value !== null && !isNaN(value)) {
                setWaterDrank(parseInt(value));
            }
        } catch (e) {
            // error reading value
        }
    };
    // init stored water data
    getStoredWater();

    // store settings
    const storeSettings = async (daily, bottle) => {
        try {
            await AsyncStorage.setItem('dailyGoalSetting', daily);
            await AsyncStorage.setItem('bottleSetting', bottle);
        } catch (e) {
            // saving error
        }
    };
    const getStoredSettings = async () => {
        try {
            const valueDailyGoalSetting = await AsyncStorage.getItem(
                'dailyGoalSetting'
            );
            const valueBottleSetting = await AsyncStorage.getItem(
                'bottleSetting'
            );
            if (
                valueDailyGoalSetting !== null &&
                !isNaN(valueDailyGoalSetting) &&
                valueBottleSetting !== null &&
                !isNaN(valueBottleSetting)
            ) {
                setDailyGoal(valueDailyGoalSetting);
                setBottle(valueBottleSetting);
            }
        } catch (e) {
            // error reading value
        }
    };
    // init stored settings data
    getStoredSettings();

    const addWater = (amount) => {
        amount = parseInt(amount);
        if (!isNaN(amount)) {
            if (waterDrank + amount >= dailyGoal) {
                setWaterDrank(dailyGoal);
                storeWater(`${dailyGoal}`);
            } else if (waterDrank + amount <= 0) {
                setWaterDrank(0);
                storeWater(`0`);
            } else {
                setWaterDrank(waterDrank + amount);
                storeWater(`${waterDrank + amount}`);
            }
        } else {
            setWaterDrank(waterDrank + 0);
            storeWater(`${waterDrank + 0}`);
        }
    };

    const compareDate = () => {
        const firstDateIsPastDayComparedToSecond = (firstDate, secondDate) =>
            firstDate.setHours(0, 0, 0, 0) - secondDate.setHours(0, 0, 0, 0) <
            0;
        const currentDate = new Date();
        const storedDate = new Date(currentDate);

        storedDate.setDate(storedDate.getDate() - 1);

        // is the first date in the past
        if (firstDateIsPastDayComparedToSecond(storedDate, currentDate)) {
            addWater(-dailyGoal);
        }
    };
    compareDate();

    const settingsToggle = () => {
        setmenuOpen(!menuOpen);
        Keyboard.dismiss();
    };

    const settingsSave = () => {
        storeSettings(dailyGoalSettings, bottleSettings);
        getStoredSettings();
        settingsToggle();
    };

    return (
        <View style={styles.body}>
            {/* settings */}
            <View
                style={[
                    styles.settingsMenu,
                    {
                        display: `${menuOpen ? 'block' : 'none'}`,
                    },
                ]}
            >
                <SafeAreaView onPress={() => Keyboard.dismiss()}>
                    <TouchableOpacity
                        style={styles.settingsButton}
                        onPress={() => settingsToggle()}
                    >
                        <FontAwesomeIcon
                            icon={faTimes}
                            color={'black'}
                            size={24}
                        />
                    </TouchableOpacity>
                    <View style={{ paddingHorizontal: 20 }}>
                        <Text style={styles.settingsText}>
                            Daily water goal in ml
                        </Text>
                        <TextInput
                            style={styles.settingsInput}
                            onChangeText={(text) =>
                                onChangeDailyGoalSettings(text)
                            }
                            value={dailyGoalSettings}
                            keyboardType={'number-pad'}
                        />
                        <Text style={styles.settingsText}>
                            Amount of a bottle in ml
                        </Text>
                        <TextInput
                            style={styles.settingsInput}
                            onChangeText={(text) =>
                                onChangeBottleSettings(text)
                            }
                            value={bottleSettings}
                            keyboardType={'number-pad'}
                        />
                        <TouchableOpacity
                            style={styles.settingsSave}
                            onPress={() => settingsSave()}
                        >
                            <Text
                                style={{
                                    alignSelf: 'center',
                                    color: '#fff',
                                    fontWeight: 'bold',
                                }}
                            >
                                Save
                            </Text>
                        </TouchableOpacity>
                    </View>
                </SafeAreaView>
            </View>
            {/* main content */}
            <SafeAreaView style={styles.container}>
                <TouchableOpacity
                    style={styles.settingsButton}
                    onPress={() => settingsToggle()}
                >
                    <FontAwesomeIcon icon={faCog} color={'white'} size={24} />
                </TouchableOpacity>

                <View style={styles.wrapper}>
                    <Text style={styles.textWaterLeft}>
                        {dailyGoal - waterDrank}
                        <Text style={styles.textWaterLeftMeasurement}>ml</Text>
                    </Text>
                    <Text style={styles.titleText}>
                        Left to hit your daily goal!
                    </Text>
                </View>
                <View style={styles.wrapper}>
                    <View style={styles.buttonContainer}>
                        <TouchableOpacity
                            style={[
                                styles.button,
                                {
                                    backgroundColor: `${
                                        waterDrank >= dailyGoal
                                            ? '#4DAC5F'
                                            : '#0064ED'
                                    }`,
                                },
                            ]}
                            onPress={() => addWater(-bottle)}
                        >
                            <Text style={styles.buttonText}>
                                -
                                <FontAwesomeIcon
                                    icon={faTint}
                                    color={'white'}
                                />
                            </Text>
                        </TouchableOpacity>
                        <TouchableOpacity
                            style={[
                                styles.button,
                                {
                                    backgroundColor: `${
                                        waterDrank >= dailyGoal
                                            ? '#4DAC5F'
                                            : '#0064ED'
                                    }`,
                                },
                            ]}
                            onPress={() =>
                                Alert.prompt(
                                    'How much water did you drink?',
                                    '',
                                    [
                                        {
                                            text: 'OK',
                                            onPress: (amount) =>
                                                addWater(amount),
                                        },
                                    ]
                                )
                            }
                        >
                            <Text style={styles.buttonText}>+</Text>
                        </TouchableOpacity>
                        <TouchableOpacity
                            style={[
                                styles.button,
                                {
                                    backgroundColor: `${
                                        waterDrank >= dailyGoal
                                            ? '#4DAC5F'
                                            : '#0064ED'
                                    }`,
                                },
                            ]}
                            onPress={() => addWater(bottle)}
                        >
                            <Text style={styles.buttonText}>
                                +
                                <FontAwesomeIcon
                                    icon={faTint}
                                    color={'white'}
                                />
                            </Text>
                        </TouchableOpacity>
                    </View>
                </View>

                <StatusBar style="auto" barStyle="dark-content" />
            </SafeAreaView>
            <View
                style={[
                    styles.indicator,
                    {
                        height: `${Math.floor(
                            (100 / dailyGoal) * waterDrank
                        )}%`,
                        backgroundColor: `${
                            waterDrank >= dailyGoal ? '#51E66E' : '#1782FF'
                        }`,
                    },
                ]}
            ></View>
        </View>
    );
}

const styles = StyleSheet.create({
    body: {
        flex: 1,
        backgroundColor: '#152940',
    },
    container: {
        flex: 1,
    },
    wrapper: {
        flex: 1,
        justifyContent: 'flex-end',
        alignItems: 'center',
    },
    indicator: {
        position: 'absolute',
        width: '100%',
        bottom: 0,
        zIndex: -1,
    },
    textWaterLeft: {
        fontSize: 77,
        color: '#fff',
        fontWeight: 'bold',
    },
    textWaterLeftMeasurement: {
        fontSize: 18,
        fontWeight: 'normal',
        color: '#fff',
        marginTop: 200,
        marginBottom: 10,
    },
    titleText: {
        color: '#fff',
        fontSize: 18,
        marginBottom: 30,
    },
    buttonContainer: {
        flexDirection: 'row',
        justifyContent: 'space-between',
        flexWrap: 'wrap',
    },
    button: {
        flex: 1,
        margin: 10,
        backgroundColor: '#0064ED',
        borderRadius: 10,
    },
    buttonText: {
        textAlign: 'center',
        color: '#fff',
        margin: 20,
        fontSize: 18,
    },
    settingsButton: {
        height: 40,
        width: 40,
        alignSelf: 'flex-end',
        zIndex: 2,
        justifyContent: 'flex-end',
    },
    settingsMenu: {
        position: 'absolute',
        height: '100%',
        width: '100%',
        backgroundColor: '#fff',
        zIndex: 200,
    },
    settingsText: {
        marginTop: 30,
    },
    settingsInput: {
        height: 40,
        borderColor: 'gray',
        borderWidth: 1,
        paddingHorizontal: 20,
        marginTop: 10,
    },
    settingsSave: {
        marginTop: 30,
        textAlign: 'center',
        width: '100%',
        padding: 15,
        borderRadius: 10,
        backgroundColor: '#0064ED',
    },
});

Ciao, try to use useEffect hook like this: Ciao,尝试像这样使用useEffect钩子:

import React, { useState, useEffect } from 'react';
...
useEffect(() => {
   compareDate();
}, [])

In this way compareDate() will be triggered only once at component renders.这样, compareDate()只会在组件渲染时触发一次。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM