1

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.

3
  • on 2nd click of login button, it works but not on first one? Commented Jul 23, 2021 at 8:08
  • in function component button works on both first & second click, but at first click even though I see response from server that props.authenticated is true it always shows false at first time. Commented Jul 23, 2021 at 8:12
  • The code works in class based component but doesn't work in function based component. Commented Jul 23, 2021 at 8:17

1 Answer 1

2

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

Update

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.

Sign up to request clarification or add additional context in comments.

6 Comments

Thank you, seems I am close, but here when I go to login screen it instantly pop up the above alert "User isn't authenticated". Even though, I haven't typed anything in email & password field and didn't submit the form.
Now it works as expected, thank you very much. How do I solve that initial pop up.
@NoorNoori You can add additional conditions, such as don't invoke on the initial render, or checking that email and password are truthy, or probably just add additional state that you trigger when authenticating, i.e. "submitted auth request" and "became authenticated".
can you please update the answer, why it is showing that default pop up? thanks for helping
hi @DrewReese, I tried to create a similar app and I don't seem to face any such error. I get the values on the first click directly. could you please help me to understand your answer and why is it that my app runs fine on first click also? thanks. codesandbox.io/s/red-cdn-eg66n?file=/src/App.js
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.