ReactJS App With Laravel RESTful API Endpoint Part 2

David Aderemi Adelekan
codeburst
Published in
8 min readApr 19, 2017

--

This article is the concluding part of the series on React App With Laravel RESTful API Endpoint. Sorry for the delay of this much awaited post.

In the previous article, I set up the react app with the Home Users and Articles pages. For this part, i would just be working on the CRUD operations on the users part of the system. This can be applied to the articles section for those who want to try it out. Before we start I want to explain a key technology i would be using in this project which is Redux.

Redux inspired by Facebook’s flux was created as an application’s state management tool. This maintains the state of an entire application in a single immutable state tree, which can’t be changed directly. You can visit http://redux.js.org/ to learn about Redux. So lets dive in

Step 1

Update the package.json file

package.json{
"name": "my-blog-reactapp",
"version": "0.0.0",
"description": "",
"main": "webpack.config.js",
"dependencies": {
"axios": "^0.15.2",
"babel-core": "^6.17.0",
"babel-loader": "^6.2.0",
"babel-plugin-add-module-exports": "^0.1.2",
"babel-plugin-react-html-attrs": "^2.0.0",
"babel-plugin-transform-class-properties": "^6.3.13",
"babel-plugin-transform-decorators-legacy": "^1.3.4",
"babel-preset-es2015": "^6.3.13",
"babel-preset-react": "^6.3.13",
"babel-preset-stage-0": "^6.3.13",
"flux": "^3.1.0",
"history": "^4.4.0",
"react": "^15.0.2",
"react-addons-css-transition-group": "^15.4.2",
"react-dom": "^15.3.2",
"react-notifications": "^1.3.0",
"react-redux": "^5.0.3",
"react-router": "^3.0.0",
"redux": "^3.6.0",
"redux-devtools": "^3.3.2",
"redux-devtools-extension": "^2.13.0",
"redux-logger": "^2.8.2",
"redux-promise": "^0.5.3",
"redux-thunk": "^2.1.0",
"webpack": "^1.12.9",
"webpack-dev-server": "^1.16.2"
},
"devDependencies": {
"font-awesome": "^4.7.0"
},
"scripts": {
"dev": "webpack-dev-server",
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "Adelekan David",
"license": "ISC"
}

Then from the terminal run

$ npm install

to update the dependencies needed for the project into a folder called node_modules.

Also inside the public/js/ folder, create a file called store.js,

store.jsimport { createStore, applyMiddleware } from 'redux';
import { composeWithDevTools } from 'redux-devtools-extension';
import thunk from "redux-thunk";
import promise from "redux-promise";
import createLogger from 'redux-logger';
import reducer from "./reducers";
const logger = createLogger();export default createStore(reducer, composeWithDevTools(
applyMiddleware(thunk, promise, logger),
));

This file is the store that holds the application state and provides a few helper methods to access the state, dispatch actions and register listeners. The entire state is represented by a single store. Any action returns a new state via reducers. That makes Redux very simple and predictable. If redux chrome developers tools is installed on your browser, you can view the store and manage states in it.

Redux chrome developers tool

Next we create the following files in the public/js/reducers folder

index.jsimport { combineReducers } from "redux"import users from "./UsersReducer"export default combineReducers({
users
})
UsersReducer.jsexport default function reducer(state={
users: [],
user: null,
fetched: false,
error: null
}, action){
switch (action.type){case "FETCH_USERS_REJECTED": {
return {
...state,
fetched: false,
error: action.payload
}
}
case "FETCH_USERS_FULFILLED": {
return {
...state,
fetched: true,
users: action.payload.data.users,
}
}
case "FETCH_USER_REJECTED": {
return {
...state,
fetched: false,
error: action.payload
}
}
case "FETCH_USER_FULFILLED": {
return {
...state,
fetched: true,
user: action.payload
}
}
}return state;}

Next we update the app.js file in the public/js/ folder

app.js

import React from "react";
import ReactDOM from "react-dom";
import { Router, Route, IndexRoute, hashHistory } from "react-router";
import { Provider } from "react-redux";
import Layout from "./components/Layout";
import Home from "./components/Home";
import Users from "./components/Users";
import NewUser from "./components/NewUser";
import EditUser from "./components/EditUser";
import Articles from "./components/Articles";
import store from "./store";
const app = document.getElementById('app');ReactDOM.render(
<Provider store={store}>
<Router history = { hashHistory }>
<Route path = "/" component = { Layout }>
<IndexRoute component = { Home }></IndexRoute>
<Route path = "users" component = { Users }></Route>
<Route path = "users/new" component = { NewUser }></Route>
<Route path = "users/:id/edit" component = { EditUser }></Route>
<Route path = "articles" component = { Articles }></Route>
</Route>
</Router>
</Provider>,
app);

Wrapping the router with a react-redux Provider component which would allow our React components to automatically share data and efficiently update whenever some action is triggered.

Step 1

Update the Users.js inside the public/js/components folder

Users.jsimport React from 'react';
import { connect } from "react-redux";
import { Link } from "react-router"
import { fetchUsers, deleteUser } from "./../actions/userActions";
class Users extends React.Component{constructor(){
super();
this.handleBtnDelete = this.handleBtnDelete.bind(this);
}
componentWillMount(){this.props.dispatch(fetchUsers());}handleBtnDelete(id, event){
event.preventDefault();
var r = confirm("Are you sure you want to delete this document!");
if (r == true) {
const url = baseUrl+"/api/v1/users/delete";
var formElement = document.getElementById("form_"+id);
var formData = new FormData(formElement);
this.props.dispatch(deleteUser(formData));
}
}
render(){return(
<div>
<h1 className="pull-left">Users</h1>
<div className="col-lg-12">
<Link to="users/new" className="btn btn-primary btn-sm pull-left">Create New &nbsp; <i className="glyphicon glyphicon-plus"></i></Link><table className="table table-responsive"><thead>
<tr>
<th>Name</th>
<th>Email</th>
<th>Phone Number</th>
<th>Contact Address</th>
<th></th>
</tr>
</thead>
<tbody>
{ this.props.users.map((user, index) => {
return (
<tr key={index+1}>
<td>{user.fullname}</td>
<td>{user.email}</td>
<td>{user.phone_number}</td>
<td>{user.contact_address}</td>
<td>
<Link to={'users/'+user.user_id+'/edit'} className="btn btn-success btn-xs pull-left"><i className="glyphicon glyphicon-pencil"></i></Link><form id={"form_"+user.user_id} className="pull-left" method="post">
<input type="hidden" name="user_id" value={user.user_id} />
<a className="btn btn-danger btn-xs" onClick={(event) => this.handleBtnDelete(user.user_id, event)} href="#" id={user.user_id}><i className="glyphicon glyphicon-trash"></i></a>
</form>
</td>
</tr>
)
}) }
</tbody>
</table></div>
</div>
);
}
}
function mapStateToProps(state) {
return {
users: state.users.users,
}
}
export default connect(mapStateToProps)(Users)

From the code above, the Users component is connected to the redux store which has access to dispatch two actions which are fetchUsers, deleteUser in the userActions.js file. Upon connecting the component to redux store, the state is then mapped to the props of the component, which enables us to access it. Next we create the userActions.js inside the public/js/actions folder

userActions.jsimport axios from "axios";
import { NotificationManager } from 'react-notifications';
export function fetchUsers(){
return function (dispatch) {
axios.get(baseUrl+"api/v1/users")
.then((response) => {
dispatch({type: "FETCH_USERS_FULFILLED", payload: response.data});
})
.catch((error) => {
dispatch({type: "FETCH_USERS_REJECTED", payload: error});
})
}
}
export function fetchUser(id){
return function (dispatch) {
axios.get(baseUrl+"api/v1/users/"+id)
.then((response) => {
dispatch({type: "FETCH_USER_FULFILLED", payload: response.data.user});
})
.catch((error) => {
dispatch({type: "FETCH_USER_REJECTED", payload: error});
})
}
}
export function deleteUser(formData){
return function (dispatch) {
axios.post(baseUrl+"api/v1/users/delete", formData)
.then((response) => {
NotificationManager.success(response.data.message, 'Success', 5000);
dispatch(fetchUsers());
})
.catch((error) => {
NotificationManager.error("An error occured in the operation", 'Error', 5000);
})
}
}

We are using a third party http library called Axios which is a promise-based HTTP client that works both in the browser and in a node.js environment. It basically provides a single API for dealing with XMLHttpRequests and node’s http interface. Besides that, it wraps the requests using a polyfill for ES6 new’s promise syntax. In this post we’ll see how to perform HTTP requests using axios.

Step 3

Create a file called NewUser.js inside the public/js/components folder. This is the component for creating a new user.

NewUser.jsimport React from 'react';
import { push } from "react-router";
import axios from "axios";
import { NotificationManager } from 'react-notifications';
class NewUser extends React.Component{static contextTypes = {
router: React.PropTypes.object.isRequired
};
constructor(){
super();
this.state = {
errors: ''
};
this.handleSubmit = this.handleSubmit.bind(this);
}
handleSubmit(event) {
event.preventDefault();
var formElement = document.querySelector("form");
var formData = new FormData(formElement);
axios.post(baseUrl+"api/v1/users/create", formData)
.then((response) => {
this.context.router.push('/users');
NotificationManager.success('User has been created!', 'Success', 5000);
})
.catch((error) => {
var errors = error.response.data.data;
this.setState({
errors: errors
});
NotificationManager.error('Error occured during operation!', 'Error', 5000);
});
}createMarkup() {
return {__html: this.state.errors};
}
render(){var errors = '';if(this.state.errors != ''){
errors = <div class="alert alert-danger" role="alert"><div dangerouslySetInnerHTML={this.createMarkup()} /></div>
}
return(
<div>
<h1>Create New User</h1>
<div className="col-lg-8">
{errors}
<form method="post" onSubmit={this.handleSubmit}>
<div className="form-group col-lg-6">
<input className="form-control" placeholder="Full Name" name="name"/>
</div>
<div className="form-group col-lg-6">
<input className="form-control" placeholder="Email Address" name="email"/>
</div>
<div className="form-group col-lg-6">
<input type="password" className="form-control" placeholder="Password" name="password"/>
</div>
<div className="form-group col-lg-6">
<input type="password" className="form-control" placeholder="Confirm Password" name="password_confirmation" />
</div>
<div className="form-group col-lg-6">
<input className="form-control" placeholder="Phone Number" name="phone_number" />
</div>
<div className="form-group col-lg-6">
<input className="form-control" placeholder="Contact Address" name="contact_address" onChange={this.handleUpdate} />
</div>
<div className="form-group col-lg-6">
<button type="submit" className="btn btn-primary btn-block">Create User</button>
</div>
</form>
</div>
</div>
);
}
}
export default NewUser;

Next we create a file called EditUser.js inside the public/js/components folder. This is the component for editing a user.

import React from 'react';
import { connect } from "react-redux";
import { fetchUser } from "./../actions/userActions";
import { push } from "react-router";
import axios from "axios";
import { NotificationManager } from 'react-notifications';
class EditUser extends React.Component{static contextTypes = {
router: React.PropTypes.object.isRequired
};
constructor(props){
super(props);
this.state = {
errors: '',
user_id: props.params.id,
}
this.handleSubmit = this.handleSubmit.bind(this);}handleSubmit(event) {
event.preventDefault();
var formElement = document.querySelector("form");
var formData = new FormData(formElement);
formData.append('user_id', this.state.user_id);
axios.post(baseUrl+"api/v1/users/update", formData)
.then((response) => {
this.context.router.push('/users');
NotificationManager.success('User has been updated!', 'Success', 5000);
})
.catch((error) => {
var errors = error.response.data.data;this.setState({
errors: errors
});
NotificationManager.error('Error occured during operation!', 'Error', 5000);
});
}componentWillMount(){
this.props.dispatch(fetchUser(this.state.user_id));
}
createMarkup() {
return {__html: this.state.errors};
}
render(){const { user } = this.props;var errors = '';if(this.state.errors != ''){
errors = <div class="alert alert-danger" role="alert"><div dangerouslySetInnerHTML={this.createMarkup()} /></div>
}
var formElements = '';
if(user !== null){
formElements =
<div>
<div className="form-group col-lg-6">
<input className="form-control" placeholder="Full Name" name="name" defaultValue={user.fullname}/>
</div>
<div className="form-group col-lg-6">
<input className="form-control" placeholder="Email Address" name="email" defaultValue={user.email}/>
</div>
<div className="form-group col-lg-6">
<input className="form-control" placeholder="Phone Number" name="phone_number" defaultValue={user.phone_number}/>
</div>
<div className="form-group col-lg-6">
<input className="form-control" placeholder="Contact Address" name="contact_address" defaultValue={user.contact_address}/>
</div>
</div>
}
return(
<div>
<h1>Edit User</h1>
<div className="col-lg-8">
{errors}
<form method="post" onSubmit={this.handleSubmit}>
{formElements}<div className="form-group col-lg-6">
<button type="submit" className="btn btn-primary btn-block">Update User</button>
</div>
</form>
</div>
</div>
);
}
}
function mapStateToProps(state) {
return {
user: state.users.user,
}
}
export default connect(mapStateToProps)(EditUser);
User list
Delete a user
User successfully deteled
Edit a user
User successfully updated

Conclusion

If you have any comments, questions, observations or better ways of implementing this, please feel free to drop them in the comment section below. For this article i left out the explanation of the laravel part because i had done an introduction in previous articles and so the changes can be found in the source code in this repo.

Thank you.

References

--

--