Too many re-renders. in React Native - javascript

I am making a custom TextInput component and in which i apply some different styles on the basis of state hook, which will be called onFocus and onBlur events, I've seen couple of solution on internet some of them are listed here Solution and i tried some of them but none of them work for me.
NOTE: I am using Expo.
Screen.js
import InputField from '../Components/InputField'
const Screen = () => {
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
return(
<InputField
placeholder='user#example.com'
label='E-mail'
value={email}
setValue={setEmail()}
isSecure={false}
defState={false}/>
)
}
InputField.js
const InputField = ({placeholder, label, value, setValue, isSecure, defState}) => {
const [isFocused, setFocus] = useState(!!defState)
const [isBlur, setBlur] = useState(!!defState)
const handle_focus = () => {
console.log('focused')
setFocus(true)
setBlur(false)
}
const handle_blur = () => {
console.log('blur')
setBlur(true)
setFocus(false)
}
return (
<View style={isBlur ? styles.container : styles.focusContainer}>
{isFocused ? <Text style={styles.label}>{label}</Text>: null}
<View style={styles.inputCont}>
<TextInput
placeholder={placeholder}
secureTextEntry={isSecure}
value={value}
onChangeText={setValue}
onFocus={()=>handle_focus}
onBlur={()=>handle_blur}
/>
<Icon name='checkmark-sharp' size={20} color={COLORS.blue} style={{marginLeft: 'auto'}}/>
</View>
</View>
);
}
Error:
Error: Too many re-renders. React limits the number of renders to prevent an infinite loop.

In your InputField change this
onFocus={()=>handle_focus}
onBlur={()=>handle_blur}
To this
onFocus={() => handle_focus()}
onBlur={() => handle_blur()}
And also, in your Screen change this
setValue={setEmail()}
to This
setValue={(text) => setEmail(text)}

Related

Change Textinput value when rendering Using .map() React native

i am using map function to render my component which have textInput . I want to change value of textInput using onchangeText function.
//main component
const [Value0, setValue0] = useState('');
const [Value1, setValue1] = useState('');
const [Value2, setValue2] = useState('');
..
const handleOnSubmit = () => { //fired when click from this
compnent button
console.log(Value0,"Value0");
console.log(Value1,"Value1");
}
//in my return i use :
{
data.map((item, id) => {
return (
<ViewDeatilCard1 key={id} /> //data having length 4
)
})
}
<ViewDeatilCard1 key={id} setChangeText={(value)=>{`'setValue${id}${(value)}'`}} /> //this
<ViewDeatilCard1 key={id} setChangeText={()=>{`'setValue${id}'`}} /> //this
<ViewDeatilCard1 key={id} setChangeText={`'setValue${id}'`} /> //this
// none of this work
// in my component i use
export const ViewDeatilCard1 = ({
setChangeText
}) => {
console.log(setChangeText,"setChangeText");
return (
<View style={styles.container}>
<View style={styles.body}>
<FormInput
style={styles.bodyText}
labelText="Enter pick up loaction"
iconName="null"
onChangeText={setChangeText}
/>
</View>
</View>
)}
how can i change my value using this approach

Why it takes two presses to do what i want? React Native

I have an input which passes its value to a useState on its parent.
That value gets passed to other component (Custom button).
There the input data gets validated and returns to the parent in another useState if there's an error and where ("e" = error in email, "p" = error in password, "ep" = error in email and password)
Then the border color of the input is set accordingly to that response, if there is an error it turns red, otherwise it turns white.
But it only works the second time i press the button (With which everything is supposed to start)
HELP!šŸ˜£
const LoginScreen = () => {
const [email, setemail] = useState('');
const [password, setpassword] = useState('');
const [error, seterror] = useState('');
return (
<View style={styles.container}>
<Input
placeholder={"Correo :"}
setInputValue={value => setemail(value)}
color={
error.includes("e") ? '#FA8072' : 'white'
}
/>
<Input
placeholder={"ContraseƱa :"}
setInputValue={value => setpassword(value)}
color={
error.includes("p") ? '#FA8072' : 'white'
}
/>
<LoginButton data={{email: email, password: password}} setValue={value => {seterror(value)}}/>
</View>
)
}
=========================================
Input component
const Input = (props) => {
return (
<View style={styles.container}>
<Text style={styles.placeholder}>{props.placeholder}</Text>
<TextInput
style={[styles.input, {borderColor: props.color}]}
onChangeText={(value) => props.setInputValue(value)}
/>
</View>
)
}
=========================================
Button component
const LoginButton = (props) => {
const [inputError, setinputError] = useState('')
let validateData = () => {
if(!props.data.email && !props.data.password){
setinputError('ep')
}
else if(!props.data.email){
setinputError('e')
}
else if(!props.data.password){
setinputError('p')
}
else {
setinputError('')
}
}
return (
<TouchableOpacity style={styles.mainButton} onPress={() => {validateData(); props.setValue(inputError)}}>
<Text style={styles.mainButtonText}>Iniciar sesiĆ³n</Text>
</TouchableOpacity>
)
}
Because you're trying to change state twice. Actually you don't need use state to pass value at LoginButton component. Try direct call instead.
const LoginButton = (props) => {
let validateData = () => {
if(!props.data.email && !props.data.password){
props.setValue("ep");
}
else if(!props.data.email){
props.setValue('e');
}
else if(!props.data.password){
props.setValue('p');
}
else {
props.setValue('');
}
}
return (
<TouchableOpacity style={styles.mainButton} onPress={() => validateData()}>
<Text style={styles.mainButtonText}>Iniciar sesiĆ³n</Text>
</TouchableOpacity>
)
}

How to avoid re rendering text input?

I have a TextInput and I don't want it to re render every time I change the value inside it
const [WrittenVal,setWrittenVal] = useState(()=>'');
...
<TextInput
value={String(WrittenVal)}
onChangeText={text => setWrittenVal(text)}
/>
but I want to be able to change the value inside the input at the push of a button that's why I haven't just used defaultValue
any solutions??
You can use useRef to save text from text input without render , and useState to show text in input on button press:
Live example : https://snack.expo.dev/TW-fMx1-2
import React from "react";
import { SafeAreaView, StyleSheet, TextInput,TouchableOpacity,Text } from "react-native";
const UselessTextInput = () => {
const [text, onChangeText] = React.useState("");
const textRef = React.useRef('')
const textToRef = (text) =>{
textRef.current = textRef.current + text
}
const showTextInInput = () =>{
onChangeText( textRef.current )
}
console.log("render")
return (
<SafeAreaView>
<TextInput
style={styles.input}
onChangeText={textToRef}
value={text}
/>
<TouchableOpacity onPress={showTextInInput}>
<Text>SHOW TEXT IN INPUT</Text>
</TouchableOpacity>
</SafeAreaView>
);
};
const styles = StyleSheet.create({
input: {
height: 40,
margin: 12,
borderWidth: 1,
marginTop:50,
padding: 10,
},
});
export default UselessTextInput;
const inputRef = useRef();
<TextInput
ref={inputRef}
onChangeText={text => inputRef.text = text }
/>
//function to get input value
const handleSubmit = () => {
let textInputValue = inputRef.text;
}
You cannot prevent re-renders on type. But your code can be simplified to:
const [value, setValue] = useState('');
<TextInput
value={value}
onChangeText={setValue}
/>
You can't prevent re-render on input when the value change.
But you can prevent other components to be re-renderd by React.memo or useMemo hook.
And for changing value of input with button press you can do like this:
<Button onPress={() => {
setWrittenVal(""); //write value you want in ""
}}
If you have a nested component situation like the below you need to move the state into the component (under EpicComponent) the setWrittenVal will trigger a re-render on the EpicComponent. Common symptoms are the the field will lose focus when you type.
const Parent = ()=> {
const [WrittenVal,setWrittenVal] = useState(()=>'');
...
const EpicComponent = ()=> {
return (
<TextInput
value={String(WrittenVal)}
onChangeText={text => setWrittenVal(text)}
/> )
}
return (<EpicComponent/>)
}

Filtering data and returning it next to the "permanent" data - react

I added a TextField from the MUI library, and used a useRef hook to capture the value "live" as the user types something. The intention is to filter only the rates which include the characters he types. As of right now:
Object.keys(rates["rates"]) // ["EUR", "RON", "CZK", ...]
I added a form, and I want it to stay persistent, but the buttons should change dynamically. If the user has not typed anything I want to return everything (like nothing is filtered)
My try:
import React, {useEffect, useRef, useState} from 'react'
import Button from '#mui/material/Button';
import CircularProgress from '#mui/material/CircularProgress';
import Box from '#mui/material/Box';
import TextField from '#mui/material/TextField';
const RatesButton = () => {
const rateRef = useRef('')
const [rates, setRates] = useState([]);
useEffect(
() => {
fetch("https://api.vatcomply.com/rates")
.then(ratesResponse => ratesResponse.json())
.then(rates => setRates(rates));
}
, [])
if (rates.length === 0) {
return (
<Box sx={{display: 'flex', justifyContent: 'center'}}>
<CircularProgress/>
</Box>
)
}
const rateSearch = () => {
Object.keys(rates["rates"]).filter(
rate => rate.includes(rateRef.current.value)
).map(rate => {
return (
<Button>
{rate}
</Button>
)
}
)
}
return (
<>
<br/>
<TextField id="rate-search" onChange={rateSearch} inputRef={rateRef} label="Rate" variant="outlined"/>
</>
)
}
export default RatesButton
It works nicely I think, I can access the reference of the input of the user, filter all the rates that contain the letters, and map each one to a MUI Button. The problem is that they don't show somehow, and I am pretty lost, it is pretty confusing how I can return from two different functions at the same time, while keeping one persistent (the input field)
The buttons do not show unfortunately...
You should use controlled mode and store your TextField's value in a state using useState instead of useRef because changing the ref value doesn't trigger a re-render so the UI doesn't get updated. There are a lot of other wrong things in your code, I've fixed it all, feel free to ask me if you don't understand anything:
const RatesButton = () => {
const [value, setValue] = useState("");
const [rates, setRates] = useState({});
useEffect(() => {
fetch("https://api.vatcomply.com/rates")
.then((ratesResponse) => ratesResponse.json())
.then((rates) => setRates(rates.rates ?? {}));
}, []);
return (
<>
{Object.keys(rates).length === 0 && (
<Box sx={{ display: "flex", justifyContent: "center" }}>
<CircularProgress />
</Box>
)}
{Object.keys(rates)
.filter((rate) => rate.toLowerCase().includes(value.toLowerCase()))
.map((rate) => {
return <Button>{rate}</Button>;
})}
<br />
<TextField
id="rate-search"
onChange={(e) => setValue(e.target.value)}
// inputRef={value}
label="Rate"
variant="outlined"
/>
</>
);
};
Live Demo
import React, {useState} from 'react';
import { throttle } from 'lodash';
const RatesButton = () => {
const [value, setValue] = useState("");
const [rates, setRates] = useState({});
useEffect(() => {
fetch("https://api.vatcomply.com/rates")
.then((ratesResponse) => ratesResponse.json())
.then((rates) => setRates(rates.rates ?? {}));
}, []);
const handleChange = (e) => setValue(e.target.value);
// it will prevent multiple render during fast typing
const throttledChange = throttle(handleChange, 400);
return (
<>
{Object.keys(rates).length === 0 && (
<Box sx={{ display: "flex", justifyContent: "center" }}>
<CircularProgress />
</Box>
)}
{Object.keys(rates)
.filter((rate) => rate.toLowerCase().includes(value.toLowerCase()))
.map((rate) => {
return <Button>{rate}</Button>;
})}
<br />
<TextField
id="rate-search"
onChange={throttledChange} // don't use arrow function
label="Rate"
variant="outlined"
/>
</>
);
};

SearchBar from react-native-elements with FlatList only allows me to type characters one by one

I'm using SearchBar from react-native-elements in my application inside a functional component.
However there's one issue I'm unable to fix: I can only type characters one by one. I cannot continuously write my text inside my bar and then search. Instead I must type every character ony by one to find a specific word.
Here's my code:
export default function InstitutionsScreen({navigation}) {
const [institutions, setInstitutions] = useState('');
const [refresh, setRefresh] = useState(false);
const [fullData, setFullData] = useState([]);
const [value, setValue] = useState('');
useEffect(() => {
if(!institutions) {
setRefresh(false);
getInstitutions();
}
}, []);
const contains = (institutionName, query) => {
if (institutionName.includes(query)) {
return true
}
return false
}
const handleSearch = text => {
setValue(text);
const formattedQuery = text.toLowerCase()
console.log("flaco me diste " + formattedQuery)
const data = filter(fullData, inst => {
return contains(inst.name.toLowerCase(), formattedQuery)
})
setInstitutions(data);
}
const onRefresh = () => {
setRefresh(true);
getInstitutions();
};
const renderHeader = () => (
<View
>
<SearchBar
lightTheme
clearIcon
onChangeText={(text) => handleSearch(text)}
value={value}
placeholder='Buscar...' />
</View>
)
if (!institutions || refresh) {
return (
<ScrollView contentContainerStyle={{alignItems: "center", flex: 1, justifyContent: 'center'}}>
<Spinner isVisible={true} size={100} type={'Pulse'} color={'#013773'}/>
</ScrollView>
);
} else {
return (
<View>
<SafeAreaView>
<FlatList
data={institutions}
renderItem={({ item }) =>
<InstitutionItem
title={item.name}
image={require('../../../assets/hands.jpg')}
logo={require('../../../assets/institution.png')}
location={item.address}
phone={item.phone}
email={item.email}
navigation={navigation}
id={item._id}
/>}
keyExtractor={(item, index) => index.toString()}
refreshing={refresh}
onRefresh={() => onRefresh()}
ListHeaderComponent={renderHeader}
/>
</SafeAreaView>
</View>
);
}
}
I tried your code, and it doesn't seem to be something related with the SearchBar, it works as it should outside of the FlatList. The problem that you are having is that you are losing the input focus for some reason on how you are "injecting" the SearchBar into the FlatList. So what I did, was to put the SearchBar right into the code, like this:
<FlatList
data={institutions}
keyExtractor={(item, index) => index.toString()}
refreshing={refresh}
onRefresh={() => onRefresh()}
ListHeaderComponent={
<SearchBar
lightTheme
clearIcon
onChangeText={handleSearch}
value={value}
placeholder='Buscar...' />
}
/>
and it worked, you are now able to keep writing without losing the focus.
This is not a bad solution, it's an easy solution, but if this is not very to your liking, you should try to find how you can insert any component in the header of the FlatList from a function or a const. Abrazo!

Categories

Resources