0

So I am brand new to Javascript and React, I am trying to write a simple Login page that takes the login form data and sends a login request to a Django api I have created. The api should then return an HttpResponse with either 200 status or 401. I have enabled CORS in the api so they can communicate with each-other.

What I have discovered is that using correct login information the fetch is successful for the first attempt after the server is started, but consecutive login attempt always fail with a "TypeError: Failed to fetch" in the browsers console.

My Django api function:

# path 'login/'
def login_user(request):
    login_attempt = json.loads(request.body.decode('utf-8'))
    try:
        user = models.User.objects.get(email=login_attempt['email'],
                                   password=login_attempt['password'])
    except models.User.DoesNotExist:
        user = None

    if user is not None:
        return HttpResponse('Login Success', status=200)

    return HttpResponse('Unauthorised', status=401)

Login.js:

class Login extends Component {
    constructor(props) {
        super(props);
        this.state = {
            email: '',
            password: '',
        };

        this.handleEmailChange = this.handleEmailChange.bind(this);
        this.handlePasswordChange = this.handlePasswordChange.bind(this);
        this.handleSubmit = this.handleSubmit.bind(this);
        this.handleResponse = this.handleResponse.bind(this);
    }

    handleEmailChange(event) {
        this.setState({email: event.target.value})
    }

    handlePasswordChange(event) {
        this.setState({password: event.target.value})
    }

    handleResponse(res) {
        if (res.ok) {
            alert('Login Successful!');
            this.props.updateTheUser(this.state.email);
        }
        else if (res.status === 401) {
            alert('Wrong Username or Password');
        }        
    }

    sendLoginRequest(data) {
        fetch('http://localhost:8000/login/', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json'},
            body: data,
        })
        .then(this.handleResponse)
        .catch(function(error) {
            alert('Server error, please try again.');
            console.error(error);
        });
    }

    handleSubmit(event) {
        const data = `{"email": "${this.state.email}", "password": "${this.state.password}"}`
        this.sendLoginRequest(data);
    }

User.js:

class User extends Component {
    constructor(props) {
        super(props);
        this.state = {
            email: '',
            isLoggedIn: false
        }

        this.updateUser = this.updateUser.bind(this);
    }

    updateUser(email) {
        console.log(`Entered User.updateUser, email: ${email}`);
        this.setState({
            email: email,
            isLoggedIn: true
        });
    }

    render() {
        if (this.state.isLoggedIn) {
            return <Dashboard/>
        }

        return <Login updateTheUser={this.updateUser}/>
    }
}

Looking in the Network tab of the browser it shows the fetch status as Cancelled and can be seen in a screenshot here. I have also included screenshots of the requests details here and here.

a-smith96
  • 55
  • 2
  • 8
  • I guess `updateTheUser` function code is needed too. my initial suspicion is that maybe you're deleting the DOM element that initiates the request. – Mahdi Ghajary Aug 12 '20 at 13:54
  • Thanks for the reply Mahdi, I've added the `updateTheUser` function code too – a-smith96 Aug 12 '20 at 18:58

1 Answers1

0

Calling render method using this.render is a bad practice and it possibly lead to unexpected bahavior.

When you set the state using this.setState, the component automatically rerenders and you don't need to call this.render.

Occasionally in rare cases, you should use forceUpdate().

updateUser(email) {
        console.log(`Entered User.updateUser, email: ${email}`);
        this.setState({
            email: email,
            isLoggedIn: true
        });
}

EDIT: not using preventDefault can cause problems. look at here.

 handleSubmit(event) {
    event.preventDefault();
    const data = { email: this.state.email, password: this.state.password };
    this.sendLoginRequest(JSON.stringify(data));
}

I made a github repo for your question in here. my code is correctly working with the server I've mocked. you can check you server with my code, if again it's not working, then maybe there's something wrong with your server.

Mahdi Ghajary
  • 2,717
  • 15
  • 20
  • Hi again, thanks! I have removed the `this.render` but I'm still getting intermittent fetch failures. I've added more of the source code for context. – a-smith96 Aug 12 '20 at 20:37
  • @a-smith96 I really can't say what's wrong with your code. if you could create a Codesandbox, please share its link so I can take a look at it. – Mahdi Ghajary Aug 13 '20 at 10:42
  • I've created a sandbox here https://codesandbox.io/s/login-attempt-e8h7r. it's my first one though so I'm unsure how it can connect to my local Django api, let me know if it works, thanks! – a-smith96 Aug 13 '20 at 11:08