Search code examples
javascriptreactjsreact-nativereact-native-firebase

When putting new value into an input field erases a previous one


With my current code, I have two input fields and a drop down menu. When ever a value is placed into the field or modified, it clears the rest of the fields. The only one that will stay consistent is the drop down menu. I have suspicions that my useEffect hooks may be doing something, but I'm quite unsure of why. Any suggestions would be great. (FYI: storeArtic is the push to firebase)

CustomScreen.js

import React, { useState } from "react";
import { StyleSheet, Text, Keyboard, View, TouchableWithoutFeedback } from "react-native";
import { Button, Input } from "react-native-elements";
import { Dropdown } from "react-native-material-dropdown";
import { storeArtic } from '../helpers/fb-settings';

const CustomScreen = ({ route, navigation }) =>{
    //create a screen with the ability to add a picture with text to the deck of artic cards
    //add check box solution for selection of word type (maybe bubbles, ask about this)
    const articDrop = [
        {value: 'CV'},
        {value: 'VC'},
        {value: 'VV'},
        {value: 'VCV'},
        {value: 'CVCV'},
        {value: 'C1V1C1V2'},
        {value: 'C1V1C2V2'},
    ];

    const [articCard, setCard] = useState({
        word: '',
        imageUrl: '',
        aType:'',
        cType: '',
        mastery: false
    })

    return(
        <TouchableWithoutFeedback onPress={Keyboard.dismiss}>
            <View>
                <Text>Please enter the information of your custom card!</Text>
                <Input
                    placeholder="Enter valid image url"
                    value={articCard.imageUrl}
                    autoCorrect={false}
                    onChangeText={(val) => setCard({ imageUrl: val })}
                />        
                <Input
                    placeholder="Enter word or phrase"
                    value={articCard.word}
                    autoCorrect={false}
                    onChangeText={(val) => 
                        setCard({ word: val, aType: val.charAt(0).toUpperCase(), mastery: false})
                    }
                />
                <Dropdown
                    value={articCard.cType}
                    onChangeText={(text) => setCard({cType: text})}
                    label="Artic Type"
                    data={articDrop}
                />
                <Button
                //this will save the cards to the database
                    title="Save"
                    onPress={() => {
                        storeArtic({articCard})
                    }} 
                />
                <Button
                    title="Clear"
                    onPress={() => {
                        setCard({word: '', aType: '', cType: '', imageUrl: '', mastery: false});
                        navigation.navigate('Home');
                    }}
                />
            </View>
        </TouchableWithoutFeedback>
    )
}


export default CustomScreen;

HomeScreen.js

import React, { useState, useEffect } from "react";
import { StyleSheet, Text, Keyboard, TouchableOpacity, View, TouchableWithoutFeedback, Image } from "react-native";
import { Button } from "react-native-elements";
import { Feather } from "@expo/vector-icons";
import { initArticDB, setupArticListener } from '../helpers/fb-settings';


const HomeScreen = ({route, navigation}) => {
  const [ initialDeck, setInitialDeck] = useState([]);

  useEffect(() => {
    try {
      initArticDB();
    } catch (err) {
      console.log(err);
    }
    setupArticListener((items) => {
      setInitialDeck(items);
    });
  }, []);

  useEffect(() => {
    if(route.params?.articCard){
      setCard({imageUrl: state.imageUrl, word: state.word, aType: state.aType, cType: state.cType, mastery: state.mastery})
    }
  }, [route.params?.articType] );


  navigation.setOptions({
        headerRight: () => (
          <TouchableOpacity
            onPress={() =>
              navigation.navigate('Settings')
            }
          >
            <Feather
              style={styles.headerButton}
              name="settings"
              size={24}
              color="#fff"
            />
          </TouchableOpacity>
        ),
        headerLeft: () => (
          <TouchableOpacity
            onPress={() =>
              navigation.navigate('About')
            }
          >
            <Text style={styles.headerButton}> About </Text>
          </TouchableOpacity>
        ),
      });

    return(
      <TouchableWithoutFeedback onPress={Keyboard.dismiss}>
        <View style={styles.container}>
          <Text style={styles.textmenu}>Welcome to Artic Cards</Text>
          <Text style={styles.textsubmenu}>Press Start to Begin!</Text>
          <Image source={require('../assets/5-snowflake-png-image.png')}
            style={{width: 300, height: 300, alignSelf: 'center'}}/>
          <Button
            title="Start"
            style={styles.buttons}
            onPress={() => navigation.navigate('Cards',
            {passDeck: initialDeck})}
          />
          <Button
            title="Progress"
            style={styles.buttons}
            onPress={() => navigation.navigate('Progress')}
          />
          <Button
            title="Customize"
            style={styles.buttons}
            onPress={() => navigation.navigate('Customize')}
          />
        </View>
      </TouchableWithoutFeedback>
    );
};

const styles = StyleSheet.create({
  container: {
    padding: 10,
    backgroundColor: '#E8EAF6',
    flex: 1,
    justifyContent: 'center'
  },
  textmenu: {
    textAlign: 'center',
    fontSize: 30
  },
  textsubmenu:{
    textAlign: 'center',
    fontSize: 15
  },
  headerButton: {
    color: '#fff',
    fontWeight: 'bold',
    margin: 10,
  },
  buttons: {
    padding: 10,
  },
  inputError: {
    color: 'red',
  },
  input: {
    padding: 10,
  },
  resultsGrid: {
    borderColor: '#000',
    borderWidth: 1,
  },
  resultsRow: {
    flexDirection: 'row',
    borderColor: '#000',
    borderBottomWidth: 1,
  },
  resultsLabelContainer: {
    borderRightWidth: 1,
    borderRightColor: '#000',
    flex: 1,
  },
  resultsLabelText: {
    fontWeight: 'bold',
    fontSize: 20,
    padding: 10,
  },
  resultsValueText: {
    fontWeight: 'bold',
    fontSize: 20,
    flex: 1,
    padding: 10,
  },
});

export default HomeScreen;

Solution

  • Unlike class based setState, with functional components when you do setState, it will override the state with what you provide inside setState function. It is our responsibility to amend state (not overrite)

    So, if your state is an object, use callback approach and spread previous state and then update new state.

    Like this

                    <Input
                        placeholder="Enter valid image url"
                        value={articCard.imageUrl}
                        autoCorrect={false}
                        onChangeText={(val) => setCard(prev => ({ ...prev, imageUrl: val }))} //<----- like this
                    /> 
    

    Do the same for all your inputs.