首页 > 解决方案 > 在屏幕之间传递数据的问题(重新渲染过多)

问题描述

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

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 但它没有工作。也许还有另一种更好的方法来改变输入数据的状态,谢谢你的帮助:)

标签: typescriptreact-nativereact-navigationuse-state

解决方案


布尔标志

当您打电话时,addProduct(JSON.stringify(props.navigation.getParam('input')),true)您将第二个参数设置oneaddtrue. 在addProduct函数中,您setDidAddInput(oneadd);在检查之前调用if(didAddInput === true),因此它将始终true并且仍然执行无限次。

我们的目标是每次执行一次input

有了boolean旗帜,这就是你要做的。初始值为false。我们仅在它仍然false存在且未更改时添加。然后添加后,我们将其设置为true,使其不会再次运行。

你不能return products在你之前,setDidAddInput因为意志之后什么都没有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回调中。我们不返回任何东西。

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

useEffect( () => {

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

}, [input]);

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


推荐阅读