在屏幕之间传递数据的问题(重新渲染太多)

Porblem with passing data between screen (Too many re-renders)

我在屏幕之间传递数据时遇到问题。这是我正在输入的屏幕:

import React, {useState} from 'react'
import {View, TextInput, Button, StyleSheet, TouchableWithoutFeedback, Keyboard} from 'react-native'
import { useGlobal } from 'reactn';
import Card from '../components/Card'
import colors from '../constants/colors';




type Props={

navigation:any
}

const AddingScreen = (props:Props) =>{
    
    let annmountAndProductToSend:any;
    
    const [enteredProduct, setEnteredProduct] = useState<string>('');
    const [amount, setAmount] = useState<any>('');
   
    
    function enteredProductHandler(enteredText:string):void
    {
        
        setEnteredProduct(enteredText);
    }

    function amountHandler(enteredText:any):void
    {
        let amountAndProduct;
        const amm = parseInt(enteredText);

        if(isNaN(enteredText) || enteredText > 1)
        {
            setAmount(enteredText);
            amountAndProduct = enteredText + ' ' + enteredProduct;
            annmountAndProductToSend = amountAndProduct;
        }
        else setAmount('')
        

    }

    function confirmProduct()
    {
      
        props.navigation.navigate('Main',{input:enteredProduct});
        setEnteredProduct('');
        setAmount('');
        
    }

    function returnToMainScreen()
    {
        props.navigation.navigate('Main')
    }

    return(
        
       
            <TouchableWithoutFeedback onPress = {() => {Keyboard.dismiss();}}>
            <View style = {styles.inputContainer}>
                <Card style = {styles.screen} >
                    <TextInput blurOnSubmit autoCapitalize="none"
                    placeholder="Nazwa przedmiotu" 
                    style={styles.input} 
                    onChangeText={enteredProductHandler} //1.onChangeText pobiera wpisany text i wysyła do goalInputHandler
                    value={enteredProduct}
                    />
                     <TextInput keyboardType = "number-pad" 
                    placeholder="Ilość" 
                    style={styles.input} 
                    onChangeText={amountHandler} //1.onChangeText pobiera wpisany text i wysyła do goalInputHandler
                    value={amount}
                    />
                
               <View style = {styles.buttonContainer}>
                   <View style = {styles.button}>
                <Button color = {colors.accent} title = 'Dodaj' onPress = {confirmProduct}/>
                </View>
                <View style = {styles.button}>
                <Button title = 'Wyjdź' color = {colors.accent} onPress={returnToMainScreen}/>
                </View>
                </View>
                </Card> 
                
             </View>
             </TouchableWithoutFeedback>
      
        
    );
};



const styles = StyleSheet.create({

    screen:
    {
        justifyContent:'center',
        alignItems:'center',
        
    },

    inputContainer:{

        flex: 0.5, 
        justifyContent:'center',
        alignItems:'center'
      },

      input:{
        height:30,
        borderBottomColor: 'grey',
        borderBottomWidth: 1,
        marginVertical: 13,
        
      },

      buttonContainer:{
          flexDirection:'row',
          alignItems: 'center',
          justifyContent: 'space-between' ,
          width: '60%'
      },
      
      button:{
          width:'40%',
         
      }
  });

  export default AddingScreen;

这是屏幕,我在其中传递此数据(输入)以将其添加到 VirtualizedList:

import { StatusBar } from 'expo-status-bar';
import React, {useState, useContext} from 'react';
import { 
  StyleSheet, 
  View, 
  Button, 
  VirtualizedList,
  Alert,
} from 'react-native';



import GoalItem from'../components/Goalitem';
import Header from '../components/Header';
import colors from '../constants/colors';



type Props={
  navigation:any
}

//setProducts([...products,{id: Math.random().toString(), value: props.navigation.getParam('input')}]); 

const MainScreen = (props:Props) =>{

 
  const [products, setProducts]  = useState<any>([]);
  const [didAddInput, setDidAddInput] = useState<boolean>(false);
  const [amont, setAmount] = useState<any>([]);

 

  function addProduct(input:string,oneadd:boolean){

   setDidAddInput(oneadd);

  if(didAddInput === true)
  {
    setProducts([...products,{id: Math.random().toString(), value: input}]); 
    return products;
  }
  setDidAddInput(false)

  };
  
  


  function removeGoalHandler(goalId:any):void
  {
    setProducts((courseGoals: any[]) => 
    {
      return courseGoals.filter((goal:any) => goal.id !== goalId);
    });
  };


  function deleteListAlert():void
  {
    if(products.length >0)
    {
      Alert.alert(
        'Lista zostanie wyczyszczona!',
        'Czy aby na pewno chcesz wyczyścić liste?',
        [{text: 'Tak', style: 'destructive', onPress: deleteList},
         {text: 'Nie', style: 'cancel'}]);
    }

    else{
      Alert.alert(
        'Lista jest pusta.',
        'W liście nie ma żadnych przedmiotów.',
        [{text: 'Powrót', style: 'cancel'}]);

    }

  }

  function deleteList()
  {
    setProducts('')
  }

  function count():number
  {
    return 50;
  }



  return (
    
    <View style = {styles.screen}>
      <Header title="Lista zakupów"/>
      <VirtualizedList 
        keyExtractor={(item:any, index) => item.id}
        //data ={addProduct(value)} 
        getItem = {addProduct(JSON.stringify(props.navigation.getParam('input')),true)}
        getItemCount = {count}
        renderItem ={itemData => (
          <GoalItem 
          id = {itemData.item.id} 
          onDelete = {removeGoalHandler} 
          title = {itemData.item.value} 

          />
        )}
      />

<View style = {styles.buttonPosition}>
      <View style = {styles.button1}>
          <Button color = {colors.accent} title = "WYCZYŚĆ" onPress={deleteListAlert}/>
        </View>
        <View style = {styles.button}>
          <Button color = {colors.accent} title="+" onPress = {() => {
            props.navigation.navigate('Adding')
          }}/>
       </View>
      </View>
      </View>
      
  );

 
}

const styles = StyleSheet.create({
  screen:{
   flex:1,
   backgroundColor: 'white',
  },

  button:{
    width: 40,
  },

  button1:{
    width: 90,
  },

  buttonPosition:{
    padding:15,
    alignItems: 'stretch',
    justifyContent: 'space-between',
    flexDirection:'row',
    backgroundColor: colors.primary,
  },

});

export default MainScreen;





所以我的数据在这里:

getItem = {addProduct(JSON.stringify(props.navigation.getParam('input')),true)}

我把它放在 console.log 中并且发送正确,但是我在将它添加到

时遇到了问题
const [products, setProducts]  = useState<any>([]);

(产品是我的值数组)

我尝试通过在添加后将状态更改为 false 来放置第二个 useState 以避免多次添加,但它没有任何改变,我遇到错误“组件异常:重新渲染太多”,也尝试使用 globalState但它不起作用。也许还有另一种更好的方法来更改输入数据的状态,感谢您的帮助:)

布尔标志

当您调用 addProduct(JSON.stringify(props.navigation.getParam('input')),true) 时,您将第二个参数 oneadd 设置为 true。在addProduct函数中,你在检查if(didAddInput === true)之前调用了setDidAddInput(oneadd);,所以它总是true并且仍然执行无限次。

我们的目标是每 input.

执行一次

使用 boolean 标志,这就是您要做的。初始值为 false。我们只在它仍然是 false 并且没有改变的情况下添加。然后添加之后,我们设置为true,这样就不会又是运行了。

您不能在 setDidAddInput 之前 return products 因为 return 之后的任何内容都不会 运行.

function addProduct(input:string) {
   // only add if not already added
  if( didAddInput === false ) {
    setProducts([...products,{id: Math.random().toString(), value: input}]);
    // prevent adding again 
    setDidAddInput(false);
  }
  // always return products
  return products;
};

使用效果

上述解决方案有很多不理想的地方。产品的添加和所有产品的访问都在同一个函数中处理,因此我们调用该函数的次数超过了必要的次数(即使我们只添加了一次)。

一个useEffect钩子更好。我们需要确保我们设置了正确的依赖关系,以便在正确的时间产生 运行 效果。每次 input 更改时,我们都希望 运行 它。 (您也可以在依赖项中包含 setProducts,但这不会有任何区别,因为它永远不会改变)。

让我们定义我们想要的 input string 作为 useEffect.

之外的变量

addProduct 功能将存在于 useEffect 回调中。我们什么都不return。

const input: string = JSON.stringify(props.navigation.getParam('input'));

useEffect( () => {

  setProducts([...products,{id: Math.random().toString(), value: input}]);

}, [input]);

效果 运行 会自动生效,因此 products 应该使用最新数据进行更新。您的 VirtualizedList 可以直接访问 products