在屏幕之间传递数据的问题(重新渲染太多)
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
。
我在屏幕之间传递数据时遇到问题。这是我正在输入的屏幕:
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
。