Reputation: 5456
I have a login view in a React Native application:
import React, {useState, useContext, useEffect} from 'react';
import {View, StyleSheet} from 'react-native';
import {Button, TextInput, Headline} from 'react-native-paper';
import globalStyles from '../styles/global';
import axios from 'axios';
import AsyncStorage from '@react-native-community/async-storage';
import AuthContext from '../context/auth/authContext';
const Login = ({navigation, route}) => {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const {user, setUser} = useContext(AuthContext);
const setLocalStorageUser = async (user) => {
try {
await AsyncStorage.setItem('user', user);
} catch (error) {
console.log(error);
}
};
const handleNewUserPress = () => {
navigation.navigate('Signup');
}
const handleLoginPress = async () => {
try {
const loginData = {
username: email,
password: password,
}
responseData = await axios.post(loginURL, loginData);
setLocalStorageUser('user', {email: email, token: responseData.token});
useEffect(() => {
setUser({email: email, token: responseData.token});
}, []);
} catch (error) {
console.log(error);
}
navigation.navigate('Home');
}
return (
<View style={globalStyles.container}>
<TextInput style={styles.input} value={email} label="Email" onChangeText={(text) => setEmail(text)} />
<TextInput style={styles.input} value={password} label="Contraseña" onChangeText={(text) => setPassword(text)} />
<Button
style={styles.button}
mode='contained'
onPress={() => handleLoginPress()}
disabled={email=='' || password==''}
>
Enviar
</Button>
<Button icon="plus-circle" onPress={() => handleNewUserPress()}>
Nuevo Usuario
</Button>
</View>
);
}
const styles = StyleSheet.create({
input: {
marginBottom: 20,
backgroundColor: 'transparent'
},
button: {
marginBottom: 20
}
})
export default Login;
The problem is in function handleLoginPress()
when calls useEffect()
. I get this error:
[Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app
See url for tips about how to debug and fix this problem.]
I have no idea why it happens and how to solve it.
Upvotes: 2
Views: 1253
Reputation: 51
useEffect is called as a function in the main function before return your jsx, but not inside of the other function or function arrow that you are declaring in the main function. In your case :
import {Button, TextInput, Headline} from 'react-native-paper';
import globalStyles from '../styles/global';
import axios from 'axios';
import AsyncStorage from '@react-native-community/async-storage';
import AuthContext from '../context/auth/authContext';
const Login = ({navigation, route}) => {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const {user, setUser} = useContext(AuthContext);
useEffect(() => {
setUser({email: email, token: responseData.token});
}, []);
const setLocalStorageUser = async (user) => {
try {
await AsyncStorage.setItem('user', user);
} catch (error) {
console.log(error);
}
};
const handleNewUserPress = () => {
navigation.navigate('Signup');
}
const handleLoginPress = async () => {
try {
const loginData = {
username: email,
password: password,
}
responseData = await axios.post(loginURL, loginData);
setLocalStorageUser('user', {email: email, token: responseData.token});
} catch (error) {
console.log(error);
}
navigation.navigate('Home');
}
return (
<View style={globalStyles.container}>
<TextInput style={styles.input} value={email} label="Email" onChangeText={(text) => setEmail(text)} />
<TextInput style={styles.input} value={password} label="Contraseña" onChangeText={(text) => setPassword(text)} />
<Button
style={styles.button}
mode='contained'
onPress={() => handleLoginPress()}
disabled={email=='' || password==''}
>
Enviar
</Button>
<Button icon="plus-circle" onPress={() => handleNewUserPress()}>
Nuevo Usuario
</Button>
</View>
);
}
const styles = StyleSheet.create({
input: {
marginBottom: 20,
backgroundColor: 'transparent'
},
button: {
marginBottom: 20
}
})
export default Login;
Regards
Upvotes: 1
Reputation: 104
As the error says: You can set useEffect in a function component only, It's not allowed useEffect inside a function expression inside a function component. The best solution, if you want to manage the useEffect you have to handle in the body of your component function and update the state to trigger it.
Upvotes: 0
Reputation: 9885
First, you need to understand, what does the useEffect
hook does. According to the documentation:
The Effect Hook lets you perform side effects in function components
import React, { useState, useEffect } from 'react';
function Example() {
const [count, setCount] = useState(0);
// Similar to componentDidMount and componentDidUpdate:
useEffect(() => {
// Update the document title using the browser API
document.title = `You clicked ${count} times`;
});
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
It was created because we didn't have a way to manage the state inside functional components. We needed to convert the component into a class and use lifecycle methods like: componentDidMount
or componentDidUpdate
.
In your case, you don't need to use the useEffect
hook since your action is being executed when you click the button to login.
You'd like to be using useEffect
when:
Upvotes: 1
Reputation: 4362
You don't need useEffect
inside handleLoginPress
:
const handleLoginPress = async () => {
try {
const loginData = {
username: email,
password: password,
}
responseData = await axios.post(loginURL, loginData);
setLocalStorageUser('user', {email: email, token: responseData.token});
setUser({email: email, token: responseData.token});
} catch (error) {
console.log(error);
}
navigation.navigate('Home');
}
Upvotes: 0