在屏幕之间传递数据的问题(重新渲染过多) [英] Porblem with passing data between screen (Too many re-renders)
问题描述
我在屏幕之间传递数据时遇到问题.这是我输入的屏幕:
I've got a problem with passing data between screens. Here is the screen where I'm taking input:
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:
And here is the screen, where i'm passing this data (input) to add it to 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中并且它被正确发送,但是我在将它添加到
I put it in console.log and it's sent right, but i've got a problem with adding it to
const [products, setProducts] = useState<any>([]);
(产品是我的值数组)
我尝试通过在添加后将状态更改为 false 来放置第二个 useState 以避免多次添加,但它什么也没改变,我有一个错误组件异常:重新渲染太多",也尝试使用 globalState 但它不起作用.也许有另一种更好的方法来改变输入数据的状态,谢谢你的帮助:)
I try to put a second useState to avoid multiple adding by changing state to false after adding but it changes nothing, i've got an error "Component Exception: Too many re-renders", also try to use globalState but it doesn't work. Maybe there is another and better way to change the state with input data, thanks for help :)
推荐答案
Boolean Flag
当您调用 addProduct(JSON.stringify(props.navigation.getParam('input')),true)
时,您将第二个参数 oneadd
设置为 真实代码>.在
addProduct
函数中,你在检查if(didAddInput === true)
之前调用setDidAddInput(oneadd);
,所以它总是true
并且仍然执行无限次.
Boolean Flag
When you call addProduct(JSON.stringify(props.navigation.getParam('input')),true)
you are setting the second argument oneadd
to true
. In the addProduct
function, you call setDidAddInput(oneadd);
before checking if(didAddInput === true)
, so it will always be true
and still execute infinite times.
我们的目标是每个 input
执行一次.
Our goal is to execute once per input
.
使用 boolean
标志,这就是您要做的.初始值为false
.我们只在它仍然是 false
并且没有改变时添加.然后添加后,我们把它设置为true
,这样就不会再运行了.
With a boolean
flag, here's what you would do. The initial value is false
. We only add if it is still false
and has not been changed. Then after adding, we set it to true
so that it will not be run again.
您不能在 setDidAddInput
之前return products
因为在 return
之后不会运行任何东西.
You cannot return products
before you setDidAddInput
because nothing after the return
will run.
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
There is a lot that's not ideal about the above solution. The adding of a product and the accessing of all products are handled in the same function, so we are calling the function way more times than is necessary ( even though we are only adding once ).
useEffect
钩子更好.我们需要确保我们设置了正确的依赖项,以便在正确的时间运行效果.我们希望每次 input
改变时运行它.(您也可以在您的依赖项中包含 setProducts
,但它不会有任何区别,因为它永远不会改变).
A useEffect
hook is better. We need to make sure that we set the correct dependencies so that the effect is run at the right times. We want to run it every time that input
changes. (You could also include setProducts
in your dependencies, but it won't make any difference because it never changes).
让我们将 input
string
定义为 useEffect
之外的变量.
Let's define the input
string
that we want as a variable outside of the useEffect
.
addProduct 功能将存在于 useEffect
回调中.我们不返回任何东西.
The addProduct functionality will exist inside of the useEffect
callback. We don't return anything.
const input: string = JSON.stringify(props.navigation.getParam('input'));
useEffect( () => {
setProducts([...products,{id: Math.random().toString(), value: input}]);
}, [input]);
效果会自动运行,因此 products
应该使用最新数据进行更新.您的 VirtualizedList
可以直接访问 products
.
The effect runs automatically so products
should get updated with the latest data. Your VirtualizedList
can access products
directly.
这篇关于在屏幕之间传递数据的问题(重新渲染过多)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!