【问题标题】:How to skip duplicate objects in useState - React Native?如何跳过 useState 中的重复对象 - React Native?
【发布时间】:2021-12-29 20:04:12
【问题描述】:

我有一个简单的应用程序,用户可以在其中从 addAnimal.js 屏幕将动物添加到 Firebase 数据库,然后我使用 UseEffect 在主屏幕中捕捉到它。我正在使用 useState 来加载我的数据,然后使用平面列表呈现它。

每当我添加新动物时,也会添加前一个动物。例如,我展示了 1 只狗,然后我用 FAB 添加了一只新猫,所以应该有 2 只,但是当我返回时,我有 2 只狗和 1 只猫。我试图实现一个 if 语句来跳过重复但没有运气。我怎样才能做到这一点?

我也不确定我是否在 UseEffect 中正确使用了登录系统。

import React, { useState} from 'react';
import { Button, Text, View, StyleSheet, Image, Animated, Dimensions } from 'react-native';
import { FlatList, TouchableOpacity } from 'react-native-gesture-handler';
import Icon from 'react-native-vector-icons/FontAwesome';
import { onValue } from 'firebase/database';
import {signInAnonymously} from 'firebase/auth';
import { db, ref,set, auth } from '../shared/setup';
import Card from '../shared/card';
import {kutya ,macska,nyul} from '../assets';


const AllatokScreen = ({navigation, route}) => {
const [allatok, setAllat] = useState([]);

const anonymous_login =() => {
  signInAnonymously(auth)
  .then((result) => {
    console.log(result)
  })
  .catch((error) => {
    const errorCode = error.code;
    const errorMessage = error.message;
    // ...
  });
}

const loadAnimails = () => {
  //Make query from database
  var userRoot = ref(db, "users/"+auth.currentUser.uid+"/allatok");
  let updatedValue = {};

  onValue(userRoot, (snapshot) => {
    var data = snapshot.val();
    var key;
    for (var line in data) {
        key = line;
        updatedValue = {"allatID":line, "allatFaj": data[key]['AllatFaj'],allatNev: data[key]['AllatNev']};
        if (!allatok.includes(updatedValue)) {
          
          //Not sure what to use here, I try to skip the duplicates
          //setAllat(allatok.concat(updatedValue))
          //setAllat(prevArray => [...prevArray, updatedValue])
        }
    }
  });
}


useEffect(() => {
  onAuthStateChanged(auth, user => {
    if (user != null) {
      Animated.timing( animation, { toValue:2, duration:300, useNativeDriver:false}).start();

      //Found User
      console.log('We are authenticated now!');
      console.log("User ID: "+user.uid)

  //Load animals
      loadAnimails();

    }
  });
},
[])



return (        
  <View style={[styles.container,styles.center]}>

    <View style={styles.center}>

    {(() => {
          if (Object.keys(allatok).length === 0){
              return (
                  <Text>There are no animals here yet, use the button below to add some</Text>
              )
          } else {
            return (
              <FlatList data = {allatok} renderItem={({item}) =>(
                <TouchableOpacity
                style={styles.TouchableOpacity}
                onPress={() => {navigation.navigate('AllatokScreen_to_InsideAllat', item.allatID)}}
                >
                  <Card>
                  {item.allatFaj == 'kutya'? <Image style={styles.image} source={kutya}/>: null }
                  {item.allatFaj == 'macska'? <Image style={styles.image} source={macska}/>: null }
                  {item.allatFaj == 'nyul'? <Image style={styles.image} source={nyul}/>: null }
                    <Text>
                      {item.allatNev}
                    </Text>
                  </Card>
                </TouchableOpacity>
              )} />
            )
          }
        })()}
      <TouchableOpacity
          style={styles.floatingActionButton}
          onPress={() => {navigation.navigate('AllatokScreen_to_AddAllat')}}
      >
        <Icon name="plus" size={30} color='#ffffff'/>
      </TouchableOpacity>

    </View>

  </View>
);
}

export default AllatokScreen;

这是我的 addAnimal 屏幕:

const AddAllat = ({navigation, route}) => {
//console.log(route);


const [AllatNev, setAllatNev] = React.useState('');
const [AllatFajta, setAllatFajta] = React.useState('');
const [AllatSzin, setAllatSzin] = React.useState('');
const [AllatNem, setAllatNem] = React.useState('');

//------------------DateTimePicker------------------
const [show, setShow] = useState(false);
const [date, setDate] = useState(new Date());
const [dateText, setDateText] = useState('Dátum kiválasztása')

const showPicker = () => {
  setShow(true);
};

const onChange = (event, selectedDate) => {
  const currentDate = selectedDate || date;
  setShow(Platform.OS === 'ios');
  setDate(selectedDate);

  let tempDate = new Date(currentDate);
  let fDate = tempDate.getFullYear() + '-' + tempDate.getMonth() + '-' + tempDate.getDate();
  setDateText(fDate)
};
//------------------DateTimePicker------------------

//------------------DropDownBox variables------------------
const [openNem, setOpenNem] = useState(false);
const [valueNem, setValueNem] = useState(null);
const [itemsNem, setItemsNem] = useState([
  {label: 'Hím', value: 'him'},
  {label: 'Nőstény', value: 'nosteny'}
]);

const [openFaj, setOpenFaj] = useState(false);
const [valueFaj, setValueFaj] = useState(null);
const [itemsFaj, setItemsFaj] = useState([
  {label: 'Kutya', value: 'kutya'},
  {label: 'Macska', value: 'macska'},
  {label: 'Nyúl', value: 'nyul'},
  {label: 'Gekkó', value: 'gekko'},
  {label: 'Hörcsög', value: 'horcsog'}
]);
//------------------DropDownBox variables------------------


const onClick_addUjAllat = () => {

  var allatid = "allatid" + Math.random().toString(16).slice(2)

  //Sending data to firebase db
  const reference = ref(db, "users/"+auth.currentUser.uid +"/allatok/"+allatid);
  set(reference, {
    'AllatNev': AllatNev,
    'AllatFaj': valueFaj,
    'AllatFajta': AllatFajta,
    'AllatSzin': AllatSzin,
    'AllatNem': valueNem,
    'AllatSzul': dateText
  });

  //Notify then Exit from here
  alert('New animal added!')
  navigation.goBack();
}

return (        
  <View style={styles.container}>
    <Text>USER ID: {auth.currentUser.uid}</Text>

    <TextInput
    placeholder="Állat neve"
    style={styles.textBox}
    value={AllatNev}
    onChangeText={(text) => setAllatNev(text)}
    />

  <DropDownPicker
    open={openFaj}
    value={valueFaj}
    items={itemsFaj}
    setOpen={setOpenFaj}
    setValue={setValueFaj}
    setItems={setItemsFaj}
    placeholder="Faj kiválasztása"
    dropDownDirection="BOTTOM"
    />

    <TextInput
    placeholder="Fajtája"
    style={styles.textBox}
    value={AllatFajta}
    onChangeText={(text) => setAllatFajta(text)}
    />
    <TextInput
    placeholder="Színe"
    style={styles.textBox}
    value={AllatSzin}
    onChangeText={(text) => setAllatSzin(text)}
    />

    <Text>Születési dátum: </Text>
    <TouchableOpacity style={styles.textBox} onPress={showPicker}>
      <Text style={styles.pickedDate}>{dateText}</Text>
    </TouchableOpacity>


    {show && (
    <DateTimePicker
      value={date}
      mode={'date'}
      display={Platform.OS === 'ios' ? 'spinner' : 'default'}
      onChange={onChange}
      style={styles.datePicker}
    />
  )}

    <DropDownPicker
    open={openNem}
    value={valueNem}
    items={itemsNem}
    setOpen={setOpenNem}
    setValue={setValueNem}
    setItems={setItemsNem}
    placeholder="Nem kiválasztása"
    dropDownDirection="TOP"
    containerStyle={{width: 250}}
    itemStyle={{alignItems: 'center'}}
    activeLabelStyle={{color: 'red'}}
    />

    <TouchableOpacity stlye={styles.buttonStyle} onPress={onClick_addUjAllat}> 
      <Text style={{color:'black'}}>Hozzáadás</Text>
    </TouchableOpacity>

  </View>
);
}

export default AddAllat;

【问题讨论】:

  • 项目是否可以更新,或者只是添加?如果它们无法更新,只需在更新前检查您的原始数组以查看您是否有匹配的 ID。如果可以更新它们,则需要检查每个对象是否相等。
  • 副本是添加到您的数据库中还是只是在您应用的渲染中?
  • @Abe 只是在应用程序渲染中,每当我返回主页时,UseEffect 会触发我的动物加载功能,它不会跳过重复项
  • @jnpdx 我相信我可以做到这两点,但我不知道如何......你能提供一些例子吗?这不起作用,为什么“如果(!allatok.includes(updatedValue))”。我的逻辑是当我添加动物时,我返回到函数运行的主屏幕(UseEffect-->loadAnimals()),在那里我检查数组中的对象是否在那里,如果它在那里我跳过,但是它不会跳过它...

标签: javascript android reactjs react-native react-hooks


【解决方案1】:

您在正确的轨道上,但您不能使用 if(!allatok.includes(updatedValue)),因为 updatedValue 是一个对象。相反,您可以通过它们的 allatID 字段来比较两者。

const loadAnimails = () => {
  //Make query from database
  var userRoot = ref(db, "users/"+auth.currentUser.uid+"/allatok");
  let updatedValue = {};

  onValue(userRoot, (snapshot) => {
    var data = snapshot.val();
    var key;
    for (var line in data) {
        key = line;
        updatedValue = {"allatID":line, "allatFaj": data[key]['AllatFaj'],allatNev: data[key]['AllatNev']};
        //Array.some() stops and returns true on the first item that 
        //meets the condition. Else returns false.
        if (!allatok.some(element=>element.allatID === updatedValue.allatID)) {
          setAllat([...allatok, updatedValue])
          
        }
    }
  });
}

如果您需要比较两个对象的整体,您也可以将它们转换为 JSON 并比较两个 JSONs

if (!allatok.some(element=> JSON.stringify(element) === JSON.stringify(updatedValue))) {
          setAllat([...allatok, updatedValue])
          }
   

【讨论】:

  • 这工作了一段时间,但每次我向我的 useState 添加新动物时,我的 FlatList 中仍然会出现重复的项目。我什至尝试添加 keyExtractor 来过滤重复的项目,但没有运气。例如我有一只狗,然后我添加了一只猫,然后我返回主屏幕,我有 3 只猫和 1 只狗而不是 2 项...@knicholas
  • 您是否尝试过在signInAnonymously 上设置user 状态(const [user, setUser] = useState(auth.currentUser))、setUser 并将其作为依赖项添加到您的useEffect?如果user 已更改,这只会调用loadAnimals。听起来useEffect 在每次状态更改时都会被调用。
  • 像这样的东西?: if (user != null) { loadAnimails();但是我只想在我从添加动物屏幕回来时加载我的动物。 UseEffect 对此有好处吗?
猜你喜欢
  • 2022-01-16
  • 2019-10-28
  • 2021-05-27
  • 1970-01-01
  • 2021-12-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多