Reputation: 1927
I am switching from class-based component to function-based components, but my code works in class-based component and doesn't work in the function-based component.
Here is the code:
import { connect } from "react-redux";
import * as actions from "../redux/actions/authActions";
class Login extends Component {
constructor(props) {
super(props);
this.state = {
email: "",
password: "",
};
}
authenticate() {
this.props.auth(this.state.email, this.state.password).then((response) => {
if (this.props.authenticated) {
alert("User Is Authenticated");
} else {
alert("User Isn't Authenticated");
}
});
}
render() {
return (
<View style={{ flex: 1 }}>
<TextInput
autoCapitalize="none"
keyboardType="email-address"
style={// styles here}
placeholder="Enter email"
value={this.state.email}
onChangeText={(email) => this.setState({ email })}
/>
<TextInput
autoCapitalize="none"
secureTextEntry
style={// styles here}
placeholder="Enter password"
value={this.state.password}
onChangeText={(password) => this.setState({ password })}
/>
<TouchableOpacity onPress={() => this.authenticate()}>
<Text style={{ marginTop: 20, color: "black", textAlign: "center" }}>
Login
</Text>
</TouchableOpacity>
</View>
);
}
}
const mapStateToProps = (state) => ({
isLoggedIn: state.auth.isLoggedIn,
isLoading: state.auth.isLoading,
userData: state.auth.userData,
error: state.auth.error,
authenticated: state.auth.isAuthenticated,
mainState: state,
});
const mapDispatchToProps = (dispatch) => ({
auth: (email, password) => dispatch(actions.loginUser({ email, password })),
});
export default connect(mapStateToProps, mapDispatchToProps)(Login);
Converted code to function based component
import { connect } from "react-redux";
import * as actions from "../redux/actions/authActions";
function Login({ auth, authenticated }) {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const authenticate = () => {
auth(email, password).then((response) => {
if (authenticated) {
alert("User Is Authenticated");
} else {
alert("User Isn't Authenticated");
}
});
};
return (
<View style={{ flex: 1 }}>
<TextInput
autoCapitalize="none"
keyboardType="email-address"
style={ // styles here}
placeholder="Enter email"
value={email}
onChangeText={(text) => setEmail(text)}
/>
<TextInput
autoCapitalize="none"
secureTextEntry
style={ // styles here}
placeholder="Enter password"
value={password}
onChangeText={(text) => setPassword(text)}
/>
<TouchableOpacity onPress={() => authenticate()}>
<Text style={{ marginTop: 20, color: "black", textAlign: "center" }}>
Login
</Text>
</TouchableOpacity>
</View>
);
}
const mapStateToProps = (state) => ({
isLoggedIn: state.auth.isLoggedIn,
isLoading: state.auth.isLoading,
userData: state.auth.userData,
error: state.auth.error,
authenticated: state.auth.isAuthenticated,
mainState: state,
});
const mapDispatchToProps = (dispatch) => ({
auth: (email, password) => dispatch(actions.loginUser({ email, password })),
});
export default connect(mapStateToProps, mapDispatchToProps)(Login);
Here at the Login
function. App state isn't updated at the first time, but it gets updated in subsequent attempts.
Thank you for reading and helping.
Upvotes: 1
Views: 40
Reputation: 202686
You've closed over stale state from the render cycle that authenticate
was invoked in. React state updates are asynchronous, so if you want to handle state after an update you need to do so in the next render cycle, likely in an useEffect
hook (synonymous to the class-based component's componentDidUpdate
method). When the authenticated
redux state value updates then the component is rerendered.
useEffect(() => {
if (authenticated) {
alert("User Is Authenticated");
} else {
alert("User Isn't Authenticated");
}
}, [authenticated]);
authenticate() {
auth(email, password)
.then((response) => {
console.log(response);
});
}
This useEffect
callback will display one alert or the other, each render. You can add state to make it wait until you've submitted an auth request AND have been authenticated.
const [isAuthenticating, setIsAuthenticating] = useState(false);
const [finishedAuth, setFinishedAuth] = useState(false);
useEffect(() => {
if (isAuthenticating && finishedAuth) {
alert(authenticated
? "User Is Authenticated"
: "User Isn't Authenticated"
);
}
}, [isAuthenticating, finishedAuth, authenticated]);
authenticate() {
setIsAuthenticating(true);
auth(email, password)
.then((response) => {
console.log(response);
})
.finally(() => setFinishedAuth(true));
}
These two additional state might be great candidates to instead store in your redux state, BTW.
Upvotes: 2