Backend DevelopmentFrontend Development

Building a Blog With Reactjs And Laravel Part13: Wrapping up

Building a Blog With Reactjs And Laravel finishing website

This is the last article in this series and we will complete the remaining pieces such as the registration, login and commenting functionality.

 

 

 

Authentication

Before creating the comments functionality we need to create the authentication functionality so we have to enable the user to register and login.

Create files Auth.js and Comment.js in resources/js/website/apis/ and update them as shown below:

Auth.js

import axios from "axios";

const Auth = {
  login: (data, successCb, failCb) => {
      axios.post('/login', data).then(response => {

          successCb(response);

      }).catch(err => {

          failCb(err);
      });
  },
  logout: (successCb, failCb) => {
      axios.get('/logout', {headers: {Authorization: 'Bearer ' + localStorage.getItem("user.api_token")}})
          .then(response => {
              localStorage.clear();

              successCb(response);
          }).catch(err => {
              failCb(err);
          alert(err.response.data.message);
      });
  },
  register: (data, successCb, failCb) => {
      axios.post('/register', data)
          .then(response => {
            successCb(response);
          }).catch(err => {
            failCb(err);
      });
  }
};

export default Auth;

Comment.js

import axios from 'axios';

const Comment = {
    store: (payload) => {
        return axios.post('/comments', payload, {headers: {Authorization: 'Bearer ' + localStorage.getItem("user.api_token")}});
    }
};

export default Comment;

Well, the next step is to update the register and login components we have done similar thing in the admin panel before.

Update resources/js/website/components/pages/Register.js as follows:

import React from 'react';
import Sidebar from '../partials/Sidebar';
import {Link} from "react-router-dom";
import Auth from '../../apis/Auth';
import { withRouter } from "react-router";
import "../../css/form.css";

class Register extends React.Component
{
    constructor(props)
    {
        super(props);

        this.state = {
            name: "",
            email: "",
            password: "",
            error_message: null,
            errors: null
        };

        this.handleSubmit = this.handleSubmit.bind(this);

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

    handleInput(e) {
        this.setState({
            [e.target.name]: e.target.value
        });
    }

    handleSubmit(e) {
        e.preventDefault();

        this.setState({
            error_message: null,
            errors: null
        });

        Auth.register({name: this.state.name, email: this.state.email, password: this.state.password}, (response) => {

            for (var i in response.data.user) {
                localStorage.setItem("user." + i, response.data.user[i]);

                setTimeout(() => {
                    this.props.history.push("/");
                }, 500);
            }

        }, (err) => {
            this.setState({
                error_message: err.response.data.message,
                errors: err.response.data.errors
            });
        });
    }

    render()
    {
        return (
            <div id="content-wrap">
                <div className="row">
                    <div id="main" className="eight columns">

                        <h2>Create account</h2>

                        {
                            this.state.error_message?(<div className="alert alert-danger">{this.state.error_message}</div>):null
                        }

                        <form name="contactForm" method="post" onSubmit={this.handleSubmit}>
                            <fieldset>
                                <div className="group">
                                    <label>Username</label>
                                    <input name="name" type="text" onChange={this.handleInput} value={this.state.name} placeholder="Name" />
                                    {
                                        this.state.errors && this.state.errors.name?(<div className="error-block">{this.state.errors.name[0]}</div>):null
                                    }
                                </div>
                                <div className="group">
                                    <label>Email</label>
                                    <input name="email" type="text" onChange={this.handleInput} value={this.state.email} placeholder="Email" />
                                    {
                                        this.state.errors && this.state.errors.email?(<div className="error-block">{this.state.errors.email[0]}</div>):null
                                    }
                                </div>
                                <div className="group">
                                    <label>Password</label>
                                    <input name="password" type="password" onChange={this.handleInput} value={this.state.password} placeholder="Password" />
                                    {
                                        this.state.errors && this.state.errors.password?(<div className="error-block">{this.state.errors.password[0]}</div>):null
                                    }
                                </div>

                                <button type="submit" className="submit">Register</button>
                                &nbsp;<Link to="/login">Already have account</Link>
                            </fieldset>
                        </form>

                    </div>

                    <Sidebar/>

                </div>
            </div>
        )
    }
}

export default withRouter(Register);

Also update resources/js/website/components/pages/Login.js as follows:

import React from 'react';
import Sidebar from '../partials/Sidebar';
import {Link} from "react-router-dom";
import Auth from '../../apis/Auth';
import { withRouter } from "react-router";
import "../../css/form.css";

class Login extends React.Component
{
    constructor(props)
    {
        super(props);

        this.state = {
            email: "",
            password: "",
            error_message: null,
            errors: null
        };

        this.handleSubmit = this.handleSubmit.bind(this);

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

    handleInput(e) {
        this.setState({
            [e.target.name]: e.target.value
        });
    }

    handleSubmit(e) {
        e.preventDefault();

        this.setState({
            error_message: null,
            errors: null
        });

        if(this.state.email == "" || this.state.password == "") {
            this.setState({
                error_message: "Please enter login credentials"
            });

            return false;
        }

        Auth.login({email: this.state.email, password: this.state.password}, (response) => {

            for (var i in response.data.user) {
                localStorage.setItem("user." + i, response.data.user[i]);

                setTimeout(() => {
                    this.props.history.push("/");
                }, 500);
            }

        }, (err) => {
            this.setState({
                error_message: err.response.data.message,
                errors: err.response.data.errors
            });
        });
    }

    render()
    {
        return (
            <div id="content-wrap">
                <div className="row">
                    <div id="main" className="eight columns">

                        <h2>Login</h2>

                        {
                            this.state.error_message?(<div className="alert alert-danger">{this.state.error_message}</div>):null
                        }

                        <form name="contactForm" method="post" action="" onSubmit={this.handleSubmit}>
                            <fieldset>
                                <div className="group">
                                    <label>Email</label>
                                    <input name="email" type="text" onChange={this.handleInput} value={this.state.email} placeholder="Email" />
                                    {
                                        this.state.errors && this.state.errors.email?(<div className="error-block">{this.state.errors.email[0]}</div>):null
                                    }
                                </div>
                                <div className="group">
                                    <label>Password</label>
                                    <input name="password" type="password" onChange={this.handleInput} value={this.state.password} placeholder="Password" />
                                    {
                                        this.state.errors && this.state.errors.password?(<div className="error-block">{this.state.errors.password[0]}</div>):null
                                    }
                                </div>

                                <button type="submit" className="submit">Login</button>
                                &nbsp;<Link to="/register">Create account</Link>
                            </fieldset>
                        </form>

                    </div>

                    <Sidebar/>

                </div>
            </div>
        )
    }
}

export default withRouter(Login);

resources/js/website/css/form.css

.alert-danger {
    color: red;
}

.alert-success {
    color: green;
}

.error-block {
    color: red;
}

.auth-links a {
    color: orangered !important;
    font-size: 10px !important;
}

#comments form label {
    float: left !important;
}

#comments form .message label {
    display: block !important;
}

Then let’s add the logout functionality, this is done in the Header component so open resources/js/website/components/partials/Header.js and update it as shown:

import React from 'react';
import {Link} from "react-router-dom";
import { withRouter } from "react-router";
import GlobalContext from '../../GlobalContext';
import "../../css/form.css";
import Auth from '../../apis/Auth';

class Header extends React.Component
{
    constructor(props)
    {
        super(props);

        this.state = {
            category_id: ""
        };

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

    handleLogout(e) {
        e.preventDefault();

        Auth.logout((response) => {
            this.props.history.push("/");
        }, (err) => {
            alert(err.response.data.message);
        });
    }

    componentDidMount()
    {
        if(this.props.location.pathname.indexOf("category") !== -1) {
            let path_parts = this.props.location.pathname.split("/");

            // category_id is at index 2
            this.setState({
                category_id: path_parts[2]
            });
        } else {
            this.setState({
                category_id: ""
            });
        }
    }

    componentDidUpdate(prevProps)
    {
        if (prevProps !== this.props) {
            if (this.props.location.pathname.indexOf("category") !== -1) {
                let path_parts = this.props.location.pathname.split("/");

                // category_id is at index 2
                this.setState({
                    category_id: path_parts[2]
                });
            } else {
                this.setState({
                    category_id: ""
                });
            }
        }
    }

    render()
    {
        return (
            <header id="top">

                <div className="row">

                    <div className="header-content twelve columns">

                        <h1 id="logo-text"><Link to="/">React Laravel Blog</Link></h1>
                        <p id="intro">Excellence and Perfection</p>

                    </div>

                </div>

                <nav id="nav-wrap"><a id="toggle-btn" title="Menu" href="#">Menu</a>


                    <div className="row">

                        <ul id="nav" className="nav">
                            <li className={this.props.location.pathname=='/'?'current':''}><Link to="/">Home</Link></li>
                            {
                                this.context.categories.map(category => <li className={this.state.category_id==category.id?'current':''} key={category.id}><Link to={'/category/' + category.id + '/' + category.slug}>{ category.title }</Link></li>)
                            }

                            {
                                localStorage.getItem("user.api_token")!=null?(
                                    <li className="auth-links">
                                        <a href='#' onClick={this.handleLogout}>logout</a>
                                    </li>
                                ) : (
                                    <li className="auth-links">
                                        <Link to='/login'>Login</Link>
                                        <Link to='/register'>Register</Link>
                                    </li>
                                )
                            }
                        </ul>


                    </div>

                </nav>

            </header>
        )
    }
}

Header.contextType = GlobalContext;

export default withRouter(Header);

Commenting

Update resources/js/website/components/partials/CommentForm.js like so:

import React from 'react';
import { withRouter } from "react-router";
import '../../css/form.css';
import CommentApi from '../../apis/Comment';

class CommentForm extends React.Component
{
    constructor(props)
    {
        super(props);

        this.state = {
            comment: "",
            error_message: null,
            errors: null,
            success_message: null
        };

        this.handleSubmit = this.handleSubmit.bind(this);

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

    handleInput(e) {
        this.setState({
            comment: e.target.value
        });
    }

    handleSubmit(e) {
        e.preventDefault();

        this.setState({
            error_message: null,
            errors: null
        });

        CommentApi.store({comment: this.state.comment, post_id: this.props.match.params.id}).then(response => {

            this.setState({
                success_message: response.data.message
            });

        }).catch(err => {
            this.setState({
                error_message: err.response.data.message,
                errors: err.response.data.errors
            });
        });
    }

    render()
    {
        return (
            <div className="respond">

                <h3>Leave a Comment</h3>

                {
                    this.state.error_message?(<div className="alert alert-danger">{this.state.error_message}</div>):null
                }

                {
                    this.state.success_message?(<div className="alert alert-success">{this.state.success_message}</div>):null
                }

                <form name="contactForm" id="contactForm" method="post" onSubmit={this.handleSubmit}>
                    <fieldset>

                        <div className="message group">
                            <textarea name="comment" id="cMessage" rows="10" cols="50" placeholder="Add your comment" onChange={this.handleInput} value={this.state.comment}></textarea>
                            {
                                this.state.errors && this.state.errors.comment?(<div className="error-block">{this.state.errors.comment[0]}</div>):null
                            }
                        </div>

                        <button type="submit" className="submit">Submit</button>

                    </fieldset>
                </form>

            </div>
        );
    }
}

export default withRouter(CommentForm);

Now run

npm run dev

and go to the website then try to register, login, browse categories and posts. By completing the comment form we finished this series, i added the repository url in bitbucket below so you can clone and run it.

 

Repository in bitbucket

Clone the repository from the below url:

Download Repository.

Then follow these steps to get it up and running:

-Set your db settings in .env

-composer install

-npm install

-php artisan migrate

-npm run dev

 

 

Final thoughts

Up till now in this series you have learned how to build a Single page application using Reactjs, this series covered a lot of features in Reactjs such as dealing with components, using react router, most important using the Redux state manager as we implemented modules in admin panel. Also you see how to connect this with backend in this case laravel powers the backend of our application. Try to extend this series to add more functionality like using roles and permissions and much more.

 

 

5 4 votes
Article Rating

What's your reaction?

Excited
0
Happy
3
Not Sure
0
Confused
0

You may also like

Subscribe
Notify of
guest
12 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
zahra
zahra
2 years ago

hello

I made a mistake during the steps.
If possible, send the final code of the blog to my email. Thanks

error :

Uncaught ReferenceError: mapDispatchToProps is not defined

it.davoodi@gmail.com

Trishia
1 year ago

It didn’t allow me to login . using the seeder user in the db. Even registering a new user it will not allow , there is no error message.

Rafal
Rafal
1 year ago

Hello,
thank you for very good example, a lot of hard work.
Did you think about update it to new version of Laravel and some update of npm repositories??

Shohidul
1 year ago

I cloned this file and see image….
What can i do??????

Screenshot (6).png
siti nj
siti nj
11 months ago

hello,,
i have question… in this post you create auth login for user add comments..
but i want click login for move to page /admin/login..
can you tell me how to make it happen?

siti nj
siti nj
11 months ago
Reply to  siti nj

and another question is it normal if an url # character is added? example: i write in address url only http://localhost/esakip/admin but after enter then become http://localhost/esakip/admin#/

siti nj
siti nj
11 months ago
Reply to  WebMobTuts

i use route same on your tutorial. and please give me advice how to move routes from public routes to admin routes. example: first location in public index then i want go to login page in admin route.. how to make it happen?

thanks..