Antonio Pavicevac-Ortiz
Antonio Pavicevac-Ortiz

Reputation: 7739

React.useEffect: State not updating after axios request

I am in the process of trying to convert a class Component into a functional component to leverage the React.useState hook and React.useEffect hook. The component is making requests to check if a token exists on a server.

import React, { Component } from 'react';

import { Loader, Dimmer, Transition, Message } from 'semantic-ui-react';
import axios from 'axios';

import { hasBeenVerified } from '../../store/reducers/users/index';

import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';


function Confirmation({ match, isAccountVerified, hasBeenVerified }) {
  var [token, setToken] = React.useState(null);
  var [duration, setDuration] = React.useState(500);
  var [responseMessage, setResponseMessage] = React.useState({});
  var [error, setError] = React.useState(false);

  React.useEffect(() => {
    console.log('token ', token);
    axios
      .get(`http://localhost:8016/users/confirmation/${setToken(match.params.token)}`)
      .then(response => {
        if (response.status === 200) {
          hasBeenVerified();
          setResponseMessage(response.data.msg);
          return;
        }
      })
      .catch(function(error) {
        if (error.response.status === 404) {
          setResponseMessage(error.response.data.msg);
          setError(true);
          return;
        }
        if (error.response.status === 400) {
          setResponseMessage(error.response.data.msg);
          setError(true);
          return;
        }
      });

      return () => {
       setToken(null);
       setResponseMessage({});
       setError(false);
    };
  }, [token, isAccountVerified, hasBeenVerified, setResponseMessage, setError]);
  console.log('token ', token);

  console.log('isAccountVerified ', isAccountVerified);
  console.log('match', match);
  console.log('responseMessage', responseMessage);
  return (
    <div className="login-form">
      <Transition
        visible={isAccountVerified}
        unmountOnHide={true}
        animation="scale"
        duration={duration}
      >
        {!isAccountVerified ? (
          <Dimmer active inverted>
            <Loader />
          </Dimmer>
        ) : (
          <Message success={!error} error={error} header={responseMessage[0]} />
        )}
      </Transition>
    </div>
  );
}

function mapStateToProps(state) {
  const { users } = state;
  const { isAccountVerified } = users;

  return { isAccountVerified };
}

const mapDispatchToProps = dispatch => bindActionCreators({ hasBeenVerified }, dispatch);

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(Confirmation);

I have added values to the dependency array because it is an outside value it depends on. I've also added the cleanup function prescribed as well.

Can anyone offer any insight why it doesn't render at all i.e. success, the different error results?

Upvotes: 2

Views: 2697

Answers (2)

AmerllicA
AmerllicA

Reputation: 32502

I write a common ReactJS code in the following, based on the Airbnb ESLint standard.

  • I don't understand why you import Component by using the destructuring assignment. it is a function component, so just import useState and useEffect by destructuring assignment.

  • Why you call your API call inside the component. it's not a common way, write it inside another file and import it or use redux action dispatching to call your API and get your API data from your redux. both of these ways are common.

  • Never write your content inside your JavaScript logical codes. write it in another file and just import it. it means you should separated your URLs paths. just like separating your API calls.

  • God shows his mercy, why you do not use this kindness? I mean async/await. definitely it makes all codes more readable.

  • Use optional chainging for nested values like match.params.token.

import React, { useState, useEffect } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { Loader, Dimmer, Transition, Message } from 'semantic-ui-react';
import { hasBeenVerified } from '../../store/reducers/users';
import { confirmCall } from '[pathTo]/api'; // pseudo code

const Confirmation = ({
  match,
  isAccountVerified,
  hasBeenVerified
}) => {
  const [token, setToken] = useState(null);
  const [duration, setDuration] = useState(500);
  const [responseMessage, setResponseMessage] = useState({});
  const [error, setError] = useState(false);

  useEffect(async () => {
    try {
      const response = await confirmCall(match?.param?.token);
      if (response.status === 200) {
        hasBeenVerified();
        setResponseMessage(response.data.msg);
      }
    } catch (error) {
      if (error.response.status === 404) {
        setResponseMessage(error.response.data.msg);
        setError(true);
      }
      if (error.response.status === 400) {
        setResponseMessage(error.response.data.msg);
        setError(true);
      }
    }
  }, []);


  return (
    <div className="login-form">
      <Transition
        visible={isAccountVerified}
        unmountOnHide={true}
        animation="scale"
        duration={duration}
      >
        {!isAccountVerified ? (
          <Dimmer active inverted>
            <Loader />
          </Dimmer>
        ) : (
          <Message
            success={!error}
            error={error}
            header={responseMessage[0]}
          />
        )}
      </Transition>
    </div>
  );
}

const mapStateToProps = ({ users: { isAccountVerified } }) => ({
  isAccountVerified
});

const mapDispatchToProps = dispatch =>
  bindActionCreators({ hasBeenVerified }, dispatch);

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(Confirmation);

Upvotes: 1

AmerllicA
AmerllicA

Reputation: 32502

You return after an asynchronous call. so definitely your state became {}.

Change the [token, isAccountVerified, hasBeenVerified, setResponseMessage, setError] to [] and it works well.

This array is using for running the useEffect callback function for changing the values of it. you passed the functions?!

Even you destruct the Component but never use it. your code is very very noisy to read.

import React, { useState, useEffect } from 'react';

~~~

useEffect(() => {
    console.log('token ', token);
    axios.get('http://localhost:8016/users/confirmation/${setToken(match.params.token)}')
      .then(response => {
        if (response.status === 200) {
          hasBeenVerified();
          setResponseMessage(response.data.msg);
          // return;
        }
      })
      .catch(function(error) {
        if (error.response.status === 404) {
          setResponseMessage(error.response.data.msg);
          setError(true);
          // return;
        }
        if (error.response.status === 400) {
          setResponseMessage(error.response.data.msg);
          setError(true);
          // return;
        }
      });

      /* return () => { // this commented part run exactly after asynchronous axios call. you don't need this part.
       setToken(null);
       setResponseMessage({});
       setError(false);
    }; */ 
  }, []);

Upvotes: 1

Related Questions