Laravel React CRUD Tutorial

Laravel React CRUD Tutorial


Posted 5 years ago by Ryan Dhungel

Category: CRUD React Laravel

Viewed 70392 times

Estimated read time: 19 minutes

Do you want to learn how to build a Laravel React CRUD (create, read, update, delete) application using Laravel and React JS? If so, I am here to help you out. This tutorial is clear and easy to read and follow along. We will build a Task Manager App, where users will be able to manage their daily tasks. So let's begin! This tutorial is also available as a video course.

prerequisite

  • Basic understanding of Laravel
  • Be able to create a fresh new Laravel project
  • Basic understanding of JavaScript/EcmaScript and React JS (or learn from here)

Create a new Laravel project with React preset

// create a new laravel project called crud
laravel new crud

// once done, get inside crud folder
cd crud

// laravel comes with vue js by default, lets change that to react
php artisan preset react

// install necessary packages from npm (node package manager)
npm install

// re-compile javascript assets every time you make a change (compulsory)
npm run watch

Connect Laravel app to mySql database

First go to your database client, I am using sequel pro (mac). Create a new database called crud. Then make changes in .env

// .env
DB_DATABASE=crud
DB_USERNAME=root
DB_PASSWORD=

Show React component in your laravel project

In your project folder directory, go to resources/assets/js/components/Example.js

There you see Example component, which comes by default with laravel. It is using bootstrap4 styling. All this html looking code is in fact JSX (mix of javascript and html). It is almost like HTML. One difference you will notice that class is being replaces with className.

Last two lines shows you that if there is an html element with an id of example (could be anywhere in our laravel app views or blade templates), then that is where this react component will render.

If you want to get a deeper understanding of Modern JavaScript/EcmaScript and React JS, make sure to checkout my course on Udemy. This course covers all the core concepts of Modern JavaScript and React JS so that you can get started comfortably in very short time. By the end you will learn to build a Twitter like Real time web app too.

Use this link to get this course for only $9.99.

Ok. enough self promotion :) Lets see how we can display this react component in our laravel app. But before that, lets generate login system so that it is done and ready for us to use.

Since we are using npm run watch, lets leave this terminal window and instead open a new terminal window, get inside our project and run the following commands.

// generate authentication
php artisan make:auth

// migrate
php artisan migrate

Now that we have auth system ready to use, lets begin by registering a new user. once done login to the application so that user is landed in the home http://crud.test/home page.

Go to resources/views/home.blade.php

Remove the existing markup and instead show the react component. For that we need to create a div with an id of example. Do the following.

@extends('layouts.app')

@section('content')
<div class="container">
    <div id="example"></div>
</div>
@endsection

Beautiful. Now go to the browser and refresh the page. There you see Example component. That is react component we are rendering!

Modifying React components file/folder structure

We are off to a great start but I am sure you don't want to continue using Example component, right? Lets make some changes. Let's rename this component to App.js and also create a new file called index.js inside resources/assets/js which will be responsible for rendering and also holds routing system (we will wok on this later).

  • Rename Example.js to App.js

  • In App.js change the component name from Example to App

  • Then in index.js, use the div with id of root instead of example

resources/assets/js/components/App.js

import React, { Component } from 'react';
import ReactDOM from 'react-dom';

export default class App extends Component {
    render() {
        return (
            <div className="container">
                <div className="row justify-content-center">
                    <div className="col-md-8">
                        <div className="card">
                            <div className="card-header">App Component</div>

                            <div className="card-body">I'm an App component!</div>
                        </div>
                    </div>
                </div>
            </div>
        );
    }
}

resources/assets/js/index.js

import React, { Component } from 'react';
import ReactDOM from 'react-dom';
// import App component
import App from './components/App'

// change the getElementId from example to app 
// render App component instead of Example
if (document.getElementById('root')) {
    ReactDOM.render(<App />, document.getElementById('root'));
}

resources/assets/js/app.js (this is not the same App.js inside components folder)

This app.js comes with laravel by default, which is inside assets/js folder. Here it is requiring Example component like so: require('./components/Example');

Change this to use index.js like so:

require('./index');

resources/views/home.blade.php

  • Change the div id from example to root
@extends('layouts.app')

@section('content')
<div class="container">
    <div id="root"></div>
</div>
@endsection

This is perfect setup. Now we can start building our awesome CRUD app. You can go to the browser and refresh to make sure everything is working. 

Creating a form in React component

Now it is time for us to build a form in our React componnet App.js. Here is the code:

export default class App extends Component {
    render() {
        return (
            <div className="container">
                <div className="row justify-content-center">
                    <div className="col-md-8">
                        <div className="card">
                            <div className="card-header">Create Task</div>
                            <div className="card-body">
                                <form>
                                    <div className="form-group">
                                        <textarea
                                            className="form-control"
                                            rows="5"
                                            placeholder="Create a new task"
                                            required
                                        />
                                    </div>
                                    <button type="submit" className="btn btn-primary">
                                        Create Task
                                    </button>
                                </form>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        );
    }
}

Here, all we did is create a form with a textarea and a button. But this form does not do anything yet. Let's put some life into it.

Make sure to have npm run watch running in your terminal. Otherwise you will not see the changes in the browser. You could also run npm run dev to compile the JavasScript but that would not be practical because we want to see the changes immediately.

Handling onChange event in React

The first thing we need to do here is handle onChange event. When a user starts typing in textarea, We want to capture the input and store in react component's state. Then we need to take that input from the state and apply into form's textarea as it's value. This is also known as controlled component, we control the textarea's value based on the component state.

In App component, create a constructor and create a local state object with two properties name and tasks. tasks value is an empty array for the moment. name property will hold the value of user input. As the input changes name property's value will change. Later we will make a post request to laravel backend with the value that is available in this name property.

We have also created handleChange method that will get the user input using onChange event and set the state using setState method. Also we need to bind this method to the constructor using bind() method.

If all this does not make sense then you need to refresh your knowledge of Modern JavaScript/EcmaScript. React is all about using modern JavaScript. I recommend you to check out my course that covers everything you need to know about modern JavaScript so that you are up and running with React in very short time.

App.js

export default class App extends Component {
    constructor(props) {
        super(props);
        this.state = {
            name: '',
            tasks: []
        };
        // bind
        this.handleChange = this.handleChange.bind(this);
    }
    // handle change
    handleChange(e) {
        this.setState({
            name: e.target.value
        });
        console.log('onChange', this.state.name);
    }

     render() {

 Inside App component's render method. I have added onChange and value to textarea. Also add maxlength to 255, this is an easy way to do a bit of validation directly in HTML.

<textarea
  onChange={this.handleChange}
  value={this.state.name}
  className="form-control"
  rows="5"
  maxLength="255"
  placeholder="Create a new task"
  required
/>

Now if you type something in the textarea, you can see the output in the console. It changes as soon as user types in. I also recommend you to install react devtools chrome extension. Where you can see the state updating as soon as there is a change.

Now we can make a post request to laravel with the value available in the state's name property right? But we have not done anything in the backend yet. So let's move on and work in the backend.

Creating Laravel API endpoints / Models / Controllers / Routing

  • Create a Task model with migration
php artisan make:model Task -m
  • Modify the migration - Add user_id and name(for tasks)
public function up()
    {
        Schema::create('tasks', function (Blueprint $table) {
            $table->increments('id');
            $table->integer('user_id')->unsigned()->index();
            $table->string('name');
            $table->timestamps();
        });
    }
  • Make the name field fillable in Task model
// app/Task,php
class Task extends Model {
	protected $fillable = ['name'];
}
  • Define a relationship between User and Task model
// Task.php
class Task extends Model {
	protected $fillable = ['name'];

    public function user() {
        return $this->belongsTo(User::class);
    }
}
// User.php
public function tasks() {
	return $this->hasMany(Task::class);
}
  • Run the migration
php artisan migrate
  • Create necessary routes
// using resource route
Route::resource('tasks', 'TaskController');
  • Create TaskController
php artisan make:controller TaskController --resource

Creating Controller to return the json response in Laravel

Lets begin by working on two methods index and store. This way we can send post request from React component to create a new post. We can also send the json response of all tasks to React component to display all tasks in the browser. For this, we have created index method. See the code below:

app/Http/Controllers/TaskController.php

<?php

namespace App\Http\Controllers;

use App\Task;
use Illuminate\Http\Request;

class TaskController extends Controller {
 // apply auth middleware so only authenticated users have access
	public function __construct() {
		$this->middleware('auth');
	}

	public function index(Request $request, Task $task) {
		// get all the tasks based on current user id
		$allTasks = $task->whereIn('user_id', $request->user())->with('user');
		$tasks = $allTasks->orderBy('created_at', 'desc')->take(10)->get();
		// return json response
		return response()->json([
			'tasks' => $tasks,
		]);
	}

	public function create() {
		//
	}

	public function store(Request $request) {
		// validate
		$this->validate($request, [
			'name' => 'required|max:255',
		]);
		// create a new task based on user tasks relationship
		$task = $request->user()->tasks()->create([
			'name' => $request->name,
		]);
		// return task with user object
		return response()->json($task->with('user')->find($task->id));
	}

	public function show($id) {
		//
	}

	public function edit($id) {
		//
	}

	public function update(Request $request, $id) {
		//
	}

	public function destroy($id) {
		//
	}
}

Back to React component, App.js. Lets create onSubmit method. This method will submit the new task to '/tasks' route, whhich is an API endpoint to make post request.

Post request from React Component to Laravel backend

App.js

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

// create handleSubmit method right after handleChange method
handleSubmit(e) {
        // stop browser's default behaviour of reloading on form submit
        e.preventDefault();
        axios
            .post('/tasks', {
                name: this.state.name
            })
            .then(response => {
                console.log('from handle submit', response);
            });
    }

Add onSubmit method to form

<form onSubmit={this.handleSubmit}>

Great! Now if you try creating a new task. It will save in the database and return a response, which you can see in the console. The returned response has data property which contains the task we just created.

Not only that, it also contains user object because we returned the response with user in our TaskController store method. Awesome!

Store the response data(tasks) in the react component state

Once we successfully create a new task, we get the response. That response we can store in an array called tasks which is in react state (we created earlier tasks: [] )

Make the following changes to handleSubmit() method:

handleSubmit(e) {
        // stop browser's default behaviour of reloading on form submit
        e.preventDefault();
        axios
            .post('/tasks', {
                name: this.state.name
            })
            .then(response => {
                console.log('from handle submit', response);
                // set state
                this.setState({
                    tasks: [response.data, ...this.state.tasks]
                });
                // then clear the value of textarea
                this.setState({
                    name: ''
                });
            });
    }

Here, we are using ... three dots (spread operator) to spread out the tasks in an array along with the existing tasks that are or will be available in the state and finally merge. Again, If you need to brush up your JavaScript skill, check out this course which I have recently published.

Render the list of tasks in react component

Now that we will have tasks in the local state as a response each time we create a new one, lets render them. (soon we will use React's lifecycle method to fetch all the tasks and display)

  • Create a renderTasks() method right below handleSubmit() method in App.js. This method uses map function, which takes function as argument which returns each task available in the state. The arrow function is used here instead of regular function. Learn the core Modern JavaScript/EcmaScript here, if necessary :)
// render tasks
    renderTasks() {
        return this.state.tasks.map(task => (
            <div key={task.id} className="media">
                <div className="media-body">
                    <p>{task.name}</p>
                </div>
            </div>
        ));
    }
  • Bind renderTasks() method to the constructor
this.renderTasks = this.renderTasks.bind(this);
  • Execute renderTasks() method right below the closing </form> tag inside render method: 
<hr />
{this.renderTasks()}

WOW! Go to the browser and give it a try! Each time you create a new task, it is displayed right below the create form. Isn't this exciting?

But there is bit more to do. As soon as you refresh the page, Its gone. That's because, It is based on each submit response. Lets fetch all the posts and render using React's lifecycle method.

React lifecycle method - componentWillMount()

There are few lifecycle methods made availabel for use to use by React JS. The most common ones are componentWillMount(), componentDidMount() etc. Feel free to read more about React's lifecycle method in the official documentaion.

Just before the React component will mount or be ready for the user to see, we want to make a get request to get all the tasks from the backend. For that we can use componentWillMount() method. Lets do this!

Create a method that will fetch all the tasks from backend (we dont need to bind this method to the constructor because it will not be used inside render() method. render() is also one of the lifecycle method).

Below we are creating getTasks() method to get all tasks from backend. Then execute this method inside componentWillMount() lifecycle method.

Write this bit of code right below the renderTasks() method in App.js

// get all tasks from backend
    getTasks() {
        axios.get('/tasks').then((
            response // console.log(response.data.tasks)
        ) =>
            this.setState({
                tasks: [...response.data.tasks]
            })
        );
    }
    // lifecycle method
    componentWillMount() {
        this.getTasks();
    }

Now go to the browser and see the awesome app you have build so far. All the tasks you created so far are available right below the create form. As you create more tasks, They are displayed immediately.

Laravel React - Send delete request and update local state

  • It is time for us to implement delete feature. Firstly, we need to delete task from the local state and update the component without page reload.
  • Next, we need to send delete request to laravel backend with the current task id, so that it can be deleted.

Create handleDelete() method right below componentWillMount() method in App.js

// handle delete
    handleDelete(id) {
        // remove from local state
        const isNotId = task => task.id !== id;
        const updatedTasks = this.state.tasks.filter(isNotId);
        this.setState({ tasks: updatedTasks });
        // make delete request to the backend
        axios.delete(`/tasks/${id}`);
    }

Then bind this method to the constructor

this.handleDelete = this.handleDelete.bind(this);

Add the delete button to each task. We are returning each task using the renderTasks() method.

Make the following changes in renderTasks(). Here we are simply using onClick event and using arrow function inside it to execute the handleDelete() method, passing the task id to delete.

// render tasks
    renderTasks() {
        return this.state.tasks.map(task => (
            <div key={task.id} className="media">
                <div className="media-body">
                    <p>
                        {task.name}{' '}
                        <button
                            onClick={() => this.handleDelete(task.id)}
                            className="btn btn-sm btn-warning float-right"
                        >
                            Delete
                        </button>
                    </p>
                </div>
            </div>
        ));
    }

Now for this to work, make sure to write some code in the destroy() method in laravel :)

TaskController.php

public function destroy($id) {
	Task::findOrFail($id)->delete();
}

That's it! Go ahead, give it a try. This app is absolutely rocking right now!

Now there is one more step to cover that is update, right? But update part is a bit tricky. So I will try to add update part some other time ok... Cheers! Please leave your comments below for any help or suggestion.