The React login form

We need to handle the different authentication states of our application:

  • The first scenario is that the user is not logged in and cannot see any posts or chats. In this case, we need to show a login form to allow the user to authenticate themselves.
  • The second scenario is that an email and password are sent through the login form. The response needs to be interpreted, and if the result is correct, we need to save the JWT inside the localStorage of the browser for now.
  • When changing the localStorage, we also need to rerender our React application to show the logged-in state.
  • Furthermore, the user should be able to log out again.
  • We must also handle if the JWT expires and the user is unable to access any functionalities.

 

 

The result for our form looks as follows:

To get started with the login form, observe the following steps:

  1. Set up the login Mutation component. It is likely that we only need this component at one place in our code, but it is a good idea to save Apollo requests in separate files.
  2. Build the login form component, which uses the login mutation to send the form data.
  3. Create the CurrentUser query to retrieve the logged-in user object.
  4. Conditionally render the login form if the user is not authenticated or the real application like the news feed if the user is logged in.

Begin by creating a new login.js file inside the mutations folder for the client components:

import React, { Component } from 'react';
import { Mutation } from 'react-apollo';
import gql from 'graphql-tag';

const LOGIN = gql`
mutation login($email : String!, $password : String!) {
login(email : $email, password : $password) {
token
}
}`;

export default class LoginMutation extends Component {
render() {
const { children } = this.props;
return (
<Mutation
update = {(store, { data: { login } }) => {
if(login.token) {
localStorage.setItem('jwt', login.token);
}
}}
mutation={LOGIN}>
{(login, { loading, error}) =>
React.Children.map(children, function(child){
return React.cloneElement(child, { login, loading, error });
})
}
</Mutation>
)
}
}

Like in the previous mutations, we parse the query string and hand the resulting login function over to all the children of the component. We now give the loading and error states to those children, in case we want to show an error or loading message. The update function is a bit different than before. We don't write the return value in the Apollo cache, but we do need to store the JWT inside the localStorage. The syntax is pretty simple. You can directly use localStorage.get and localStorage.set to interact with the web storage.

Now, we implement the underlying children, which makes up the login form. To do this, we create a loginregister.js file directly in the components folder. As you may expect, we handle the login and registration of users in one component. Import the dependencies:

import React, { Component } from 'react';
import Error from './error';
import LoginMutation from './mutations/login';

The LoginForm class will store the form state, display an error message if something goes wrong, and send the login mutation including the form data:

class LoginForm extends Component {
state = {
email: '',
password: '',
}
login = (event) => {
event.preventDefault();
this.props.login({ variables: { email: this.state.email, password:
this.state.password }});
}
render() {
const { error } = this.props;
return (
<div className="login">
<form onSubmit={this.login}>
<label>Email</label>
<input type="text" onChange={(event) => this.setState({email:
event.target.value})} />
<label>Password</label>
<input type="password" onChange={(event) =>
this.setState({password: event.target.value})} />
<input type="submit" value="Login" />
</form>
{error && (
<Error><p>There was an error logging in!</p></Error>
)}
</div>
)
}
}

We render the login form inside the wrapping component, which is called LoginRegisterForm. It is important to surround the form with the login mutation so that we can send the Apollo request:

export default class LoginRegisterForm extends Component {
render() {
return (
<div className="authModal">
<LoginMutation><LoginForm/></LoginMutation>
</div>
)
}
}

All the basics for authenticating the user are now ready, but they have not been imported yet or displayed anywhere. Open the App.js file. There, we directly display the feed, chats, and the top bar. The user should not be allowed to see everything if he is not logged in. Continue reading to change this.

Import the new form that we have just created:

import LoginRegisterForm from './components/loginregister';

We then have to store whether the user is logged in or not. We save it in the component state, as follows:

state = {
loggedIn: false
}

When loading our page, this variable needs to be set to true if we have a token in our localStorage. We handle this inside the componentWillMount function provided by React:

componentWillMount() {
const token = localStorage.getItem('jwt');
if(token) {
this.setState({loggedIn: true});
}
}

Then, in the render method, we can use conditional rendering to show the login form when the loggedIn state variable is set to false, which means that there is no JWT inside our localStorage:

{this.state.loggedIn ?
<div>
<Bar />
<Feed />
<Chats />
</div>
: <LoginRegisterForm/>
}

If you try the login page, you will see that nothing happens, even though no error message is shown. That happens because we save the JWT, but we do not tell React to rerender our App class. We only check for the JWT when the page loads initially. To test your implementation, you can reload the window, and you should be logged in.

We have to pass a function down the React tree to the components, who are then able to trigger a logged-in state so that React can rerender and show the logged in area. We call this function changeLoginState and implement it inside the App.js file as follows:

changeLoginState = (loggedIn) => {
this.setState({ loggedIn });
}

The function can change the current application state as specified through the loggedIn parameter. We then integrate this method into the LoginMutation component. To do this, we edit the render method of the App class to pass the right property:

<LoginRegisterForm changeLoginState={this.changeLoginState}/>

Then, inside the LoginRegisterForm class, we replace the render method with the following code:

render() {
const { changeLoginState } = this.props;
return (
<div className="authModal">
<LoginMutation changeLoginState={changeLoginState}><LoginForm/></LoginMutation>
</div>
)
}

Edit the LoginMutation component and extract the new function from the properties:

const { children, changeLoginState } = this.props;

We can then execute the changeLoginState function within the update method:

if(login.token) {
localStorage.setItem('jwt', login.token);
changeLoginState(true);
}

When logging in, our application presents us with the common posts feed as before. The authentication flow is now working, but there is one more open task. In the next step, we allow new users to register at Graphbook.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.143.9.115