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

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>
)
}

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

Too many re-renders. in React Native

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)}

Re rendering a component with an async function inside

I am new to react native and my JS is a bit rusty. I need to be able to change the value of my collection for the firestore. I have two buttons that will change the value of typeOfPost by setting the state. Component1 can successfully get "this.state.typeOfPost". However, when I click one of the buttons and update the state my log inside of the async function is not being called. It is only called when the app initially renders. What I find weird is that my log on the top of Component1 will display as expected. Is there any better way of doing this?
class Forum extends Component {
state = {
typeOfPost: ' '
}
onPressSitter = () => {
this.setState({
typeOfPost: 'sitterPosts'
})
}
onPressNeedSitter = () => {
this.setState({
typeOfPost: 'needPosts'
})
}
render() {
return (
<View style={styles.container}>
<View style={styles.row}>
<TouchableOpacity
style={styles.button}
onPress={this.onPressSitter}
>
<Text>I am a sitter</Text>
</TouchableOpacity>
<TouchableOpacity
style={styles.button}
onPress={this.onPressNeedSitter}
>
<Text>Need a sitter</Text>
</TouchableOpacity>
</View>
<View>
<Component1 typeOfPost = {this.state.typeOfPost}> </Component1>
</View>
</View>
)
}
}
const Component1 = (props) => {
console.log("type of post " + props.typeOfPost);
const [loading, setLoading] = useState(true); // Set loading to true on component mount
const [data, setData] = useState([]); // Initial empty array of data
const getData = async () => {
console.log("type of post inside async " + props.typeOfPost);
const subscriber = firestore()
.collection(props.typeOfPost) // need to be able to update this
.onSnapshot(querySnapshot => {
const data = [];
querySnapshot.forEach(documentSnapshot => {
data.push({
...documentSnapshot.data(),
key: documentSnapshot.id,
});
});
setData(data);
setLoading(false);
});
// Unsubscribe from events when no longer in use
return () => subscriber();
}
useEffect(() => {
getData();
}, [])
if (loading) {
return <ActivityIndicator />;
}
return (
<FlatList
data={data}
ListEmptyComponent={
<View style={styles.flatListEmpty}>
<Text style={{ fontWeight: 'bold' }}>No Data</Text>
</View>
}
renderItem={({ item }) => (
<View>
<Text>User ID: {item.fullName}</Text>
</View>
)}
/>
)
}
There is a difference between mount and render. I see no problem with your code except the few remarks I have made. The thing is that when you change typeOfPost, the component is rerendered, but the useEffect is not called again, since you said, it's just called when it was first mounted:
useEffect(() => {
}, []) // ---> [] says to run only when first mounted
However here, you want it to run whenever typeOfPost changes. So here is how you can do this:
useEffect(() => {
getData();
}, [typeofPost])
class Forum extends Component {
state = {
typeOfPost: ' '
}
onPressSitter = () => {
this.setState({
typeOfPost: 'sitterPosts'
})
}
onPressNeedSitter = () => {
this.setState({
typeOfPost: 'needPosts'
})
}
render() {
return (
<View style={styles.container}>
<View style={styles.row}>
<TouchableOpacity
style={styles.button}
onPress={this.onPressSitter}
>
<Text>I am a sitter</Text>
</TouchableOpacity>
<TouchableOpacity
style={styles.button}
onPress={this.onPressNeedSitter}
>
<Text>Need a sitter</Text>
</TouchableOpacity>
</View>
<View>
<Component1 typeOfPost = {this.state.typeOfPost}> </Component1>
</View>
</View>
)
}
}
const Component1 = (props) => {
const { typeOfPost } = props
console.log("type of post " + props.typeOfPost);
const [loading, setLoading] = useState(true); // Set loading to true on component mount
const [data, setData] = useState([]); // Initial empty array of data
const getData = () => {
setLoading(true)
console.log("type of post inside async " + props.typeOfPost);
const subscriber = firestore()
.collection(props.typeOfPost) // need to be able to update this
.onSnapshot(querySnapshot => {
const data = [];
querySnapshot.forEach(documentSnapshot => {
data.push({
...documentSnapshot.data(),
key: documentSnapshot.id,
});
});
setData(data);
setLoading(false);
});
// Unsubscribe from events when no longer in use
return () => subscriber();
}
useEffect(() => {
getData();
}, [typeofPost])
if (loading) {
return <ActivityIndicator />;
}
return (
<FlatList
data={data}
ListEmptyComponent={
<View style={styles.flatListEmpty}>
<Text style={{ fontWeight: 'bold' }}>No Data</Text>
</View>
}
renderItem={({ item }) => (
<View>
<Text>User ID: {item.fullName}</Text>
</View>
)}
/>
)
}
you are using a class based component to access react hook which is a bad practice, i will advice you use a functional component and you have access to react useCallback hook which will handle your request easily
const ButtonPressed = useCallback(() => {
setLoading(true);
getData()
}).then(() => setLoading(false));
}, [loading]);

How to set status when click login in react-native?

I want to create Drawer when after login. I'm not sure what I'm going to do is right.
If it's correct, how do I get back to handling setIsLogin in the App.js file?
Myapp.js
import LoginScreen from './src/screens/LoginScreen';
export default function App() {
const [isLogin, setIsLogin] = useState(false);
if(isLogin){
return(
//create Drawer here
)
}else{
return(
<LoginScreen/>
)
}
}
LoginScreen.js
const LoginScreen = ({navigation}) => {
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
const _onPress = async() => {
const payload = {
username:username,
password:password
}
};
return (
<View>
<TouchableOpacity onPress={() => _onPress()} >
<Text style={styles.textButton}>Log in</Text>
</TouchableOpacity>
</View>
)
}
export default LoginScreen;
If i misunderstand Or is there a better way, please advise me, thank you very much.
Here you go, this should work:
You can pass the setState as reference from parent to child, update the state on child and it will rerender parent.
import LoginScreen from './src/screens/LoginScreen';
export default function App() {
const [isLogin, setIsLogin] = useState(false);
if(isLogin){
return(
//create Drawer here
)
}else{
return(
<LoginScreen setLoginStatus = {setIsLogin}/>
)
}
}
const LoginScreen = ({navigation, setLoginStatus}) => {
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
const _onPress = async() =>
{
const payload = {
username:username,
password:password
}
setLoginStatus(true) //Or false depending on the payload response.
};
return (
<View>
<TouchableOpacity onPress={() => _onPress()} >
<Text style={styles.textButton}>Log in</Text>
</TouchableOpacity>
</View>
)
}
export default LoginScreen;

React-Native: Dispatch on submit is not working

I am trying to do a basic todo list however when I dispatch an action after pressing add it doesn't dispatch .
I've taken the dispatch(todo(todoList))out of every function and left it in the main ToDo component to do multiple calls on every letter typed into the search box and I can see an update in my redux store in Redux-dev tools so I know my todo works but It won't dispatch() when I try to submit. Please can someone help me .
This is my code:
import {useDispatch } from 'react-redux'
import { todo } from './action/todo'
const ToDo = () => {
const [todo, setTodo] = useState('')
const [todoList, setTodoList] = useState([])
const dispatch = useDispatch()
const handleSubmit = (id , todo) => {
const newTodoList = todoList.concat({ id: id, val: todo })
return (
setTodo(''),
todo.length === 0
? <Text/>
: setTodoList(newTodoList) // if I put the dispatch here it doesn't work either
)
}
return (
<View style={styles.addPhotoCont}>
<TextInput
placeholder={props.textInputPlaceholder}
onChangeText={props => setTodo(props)}
value={todo}
/>
<TouchableOpacity
onPress={() => handleSubmit(Date.now(), todo) && dispatch(todo(todoList))}>
<Text style={styles.addButton}>Add</Text>
</TouchableOpacity>
</View>
)
}
It looks like you set todo twice, once as an import, and the second time as state. When you call dispatch and pass in todo it is calling the state version.
You should put the dispatch in the handleSubmit function.
Also, looking at the handleSubmit function, the return will not work. You can only return one thing. You can place the other functions above the return statement.
Edit:
Code sample below:
import { useDispatch } from 'react-redux'
import { todo } from './action/todo'
const ToDo = (props) => {
const [todoInputValue, setTodoInputValue] = useState('')
const dispatch = useDispatch()
const handleSubmit = (todo) => {
dispatch(todo({id: Date.now(), val: todoInputValue}))
setTodoInputValue('')
}
return (
<View style={styles.addPhotoCont}>
<TextInput
placeholder={props.textInputPlaceholder}
onChangeText={value => setTodoInputValue(value)}
value={todoInputValue}
/>
<TouchableOpacity
onPress={() => handleSubmit(Date.now())}>
<Text style={styles.addButton}>Add</Text>
</TouchableOpacity>
</View>
)
}

Categories

Resources