🔖 [ReactJS] Simple CRUD with Back4App - Create & Read

2020 - 01 - 23
🔖 [ReactJS] Simple CRUD with Back4App - Create & Read
0. [Create-React-App] ReactJS is a front-end development framework which is also famous in serverless applications. Using JSX and ES6 JavaScript, ReactJS can perform CRUD functions of a normal webapp asynchronously. In this tutorial, we are going to build a basic CRUD webapp using ReactJS. Below are the required installations for building this project:
  • npm with nodejs
  • create-react-app package
  • parse package
  • react-router-dom package
  • BACK4APP: Backend & DB cloud service (similar to Google Firebase)
So to start with, download and install npm from the link above if you don't have. Then we can open the terminal and install create-react-app:
npm install create-react-app
Now head to the directory that you wish to construct the new project folder. In this tutorial, we are going to build a webapp for adding, editing, and deleting travel records, say the name of the project folder to be "travelrecord":
create-react-app travelrecord
Once it's done, you will see "Happy hacking!" on the terminal. Now you may type cd travelrecord to go into the project folder. At the same time, we can use any text editor to open the project folder travelrecord.
1. [Components] React combined both front-end views and rendering methods (i.e. Views & Controller in MVC), we define the functions and the front-end structure in a single component instead of separating them. After opening the project folder, there is a index.js under src. This is the main document referring to the public/index.html which contains a basic HTML structure and is the only html file we need. In the index.js, it imported the App.js at the top of the document. App.js is the top component being called at ReactDOM.render(). You may notice that importing a JS file allow omitting .js at the end of the file name, such as './App' instead of './App.js'. When we have written new components, we will import them into App.js and render inside. So the structure of rendering is index -> App -> other components we write. There are many ways to write a component, such as a single function (e.g. shown in the App.js), or a class object which we are going to do. Open App.js and replace the content as follow:
import React from 'react';
import logo from './logo.svg';
import './App.css';

class App extends React.Component {
  render(){
    return(
      <div>
        <p>Hello World!</p>
      </div>
    );
  };
}

export default App;
Now run the project by typing this in the terminal (inside the project folder):
npm run start
The project will run automatically on your browser at localhost:3000! To stop running, press Ctrl + C.
2. [ParseServer Back4App] As this tutorial is creating a CRUD webapp, we need a database to save and render data, i.e. travel records in this example. Many people would use Google Firebase, but we are going to use Back4App. Before that, we have to install a package parse using npm, since Back4App use ParseObject.
npm install parse
Head to Back4App website and create an account. Click Build new app and give it a name Travel Records . On the dashboard menu, click API Reference and look for Initializing Parse SDK, from which copy the Application ID and Javascript Key for later use. Open App.js in text editor and update the content as follow. Make sure to copy and paste the Application ID and Javascript Key.
import React from 'react';
import logo from './logo.svg';
import './App.css';
import Parse from 'parse'; //Import parse

class App extends React.Component {
  constructor(){
    super();
    Parse.serverURL = 'https://parseapi.back4app.com';
    Parse.initialize(
      'PASTE_YOUR_APPLICATION_ID_HERE', // Application ID
      'PASTE_YOUR_JAVASCRIPT_KEY_HERE' // Javascript key
    );
  };

  render(){
      return(
        <div>
            <p>Hello World</p>
        </div>
      );
  };
}

export default App;

3. [Create a travel record] Now the App is ready to fetch data to and from our Back4App account. First of all, we are going to create a new piece of travel record to the DB. Add a new file Create.js under src folder, which will act as the component to create travel records. Inside this file, we have to do several things:
  • Import React
  • Make a form for data input
  • Make a function to submitting the data to the DB (actually passing a dummy record to App.js for posting)
  • Prevent form from refreshing (i.e. event.preventDefault())
Hence, the Create.js looks like this:
import React from 'react';

class Create extends React.Component {
  begin = React.createRef();
  end = React.createRef();
  place = React.createRef();

  submit = (e) => {
    e.preventDefault();

    if (this.begin.current.value !== '' &&
        this.end.current.value !== '' &&
        this.place.current.value !== '') {

        let record = {
          begin: this.begin.current.value,
          end: this.end.current.value,
          place: this.place.current.value
        };

        this.props.createRecord(record);
    }
  };

  render(){
    return(
        <form onSubmit={this.submit}>
          <label>Begin</label>
          <input name='begin' ref={this.begin} type='date' />
          <label>End</label>
          <input name='end' ref={this.end} type='date' />
          <label>Place</label>
          <input name='place' ref={this.place} type='text' />
          <button type='submit'>Add Record!</button>
        </form>
    );
  };
}

export default Create;
It should be noticed that references are created (i.e. React.createRef()) to store the input data temporarily, and used in the submit method. This is the way for getting data from Forms. Now the dummy record is passed to a function named createRecord(), which should be shared from App.js. So open App.js, make a createRecord method and share to Create component as follow:
import React from 'react';
import logo from './logo.svg';
import './App.css';
import Parse from 'parse';
import Create from './Create';

class App extends React.Component {
  constructor(){
    super();
    Parse.serverURL = 'https://parseapi.back4app.com';
      'PASTE_YOUR_APPLICATION_ID',
      'PASTE_YOUR_JAVASCRIPT_KEY'
    );
  };

  createRecord = (r) =>{
    const Travel = Parse.Object.extend('Travel');
    const record = new Travel();

    record.set('begin', r.begin);
    record.set('end', r.end);
    record.set('place', r.place);

    record.save().then(
      (result) => {
        console.log(result);
      },
      (error) => {
        console.error(error);
      }
    );
  };

  render(){
      return(
        <div>
          <p>Hello World</p>
          <Create createRecord={this.createRecord} />
        </div>
      );
  };
}

export default App;

Notice that we share the createRecord() method by adding an attribute in this line at App.js
<Create createRecord={this.createRecord} />
Such that the createRecord() method can be called at Create.js as
this.props.createRecord
Run the project again
npm run start
Now, if you input details and submit the form, a new record should be shown in the Back4App DB under class name Travel.
4. [State and properties] After creating a new travel record, you may wish to view it from the web app. Before that, we need to understand how to store data locally in the web app and propagate to other components, i.e. state. React can propagate data and methods to child components. Hence, if we define a JSON state in App.js, it can be shared to Some_Component.js. By using setState(), we can save the data in the state object and shared asynchronously to other components. To hold all travel records locally, we add a state object in App.js after constructor() method:
constructor(){
    super();
    Parse.serverURL = 'https://parseapi.back4app.com';
    Parse.initialize(
      'PASTE_YOUR_APPLICATION_ID',
      'PASTE_YOUR_JAVASCRIPT_KEY'
    );
  };

  state = {
    records: {}
  };

  createRecord = (r) => {
    //...
  };
Then add a new readRecord() method after createRecord() to render travel records from DB:
createRecord = (r) => {
  //...
};

readRecord = () => {
  const Travel = Parse.Object.extend('Travel');
  const query = new Parse.Query(Travel);
  query.find().then(
    (result) => {
      let records = JSON.parse(JSON.stringify(result)).sort(
        (a,b) => (a.begin>b.begin) ? -1 : 1
      );
      this.setState({ records }, this.forceUpdate());
    },
    (error) => {console.error(error)}
  );
};
Noticed that the result from DB will be a ParseObject, which I convert into an array and sorted by descending order. After that, the sorted array will be parsed as a JSON again, and saved to the local state by using this.setState(). Normally, the way to setState should look like this:
let abc = '123';
this.setState({ records: abc });
But the name of the state object (i.e. 'records') is the same as the variable we used for holding the result, hence we can omit the variable 'records' on the right-hand-side. Also, we added a callback function this.forceUpdate() which force the App.js to re-render since there is no guarantee that this.state would sync updated data immediately. More detail can be read here: Stack Overflow Now we can share the state object records and the readRecord() method to a new component Read.js which we will create later. Import Read.js at the top of App.js:
import Read from './Read';
And add the tag <Read> after <Create> in App.js with attributes like this:
render(){
  return(
    <div>
      <p>Hello World</p>
      <Create createRecord={this.createRecord} />
      <br />
      <Read readRecord={this.readRecord} records={this.state.records}/>
    </div>
  );
};

5. [Read all travel records] Make a new file named Read.js under src, and it should have the duties as followed:
  • call the readRecord() method once this component is mounted (i.e. loaded in the main App)
  • Render the state.records shared by the App.js
So Read.js will look like this:
import React from 'react';
import Record from './Record';

class Read extends React.Component {
  componentDidMount(){
    this.props.readRecord();
  };

  render(){
    return(
      <table>
        <tbody>
          <tr>
            <th>Begin</th>
            <th>End</th>
            <th>Place</th>
          </tr>
            {Object.keys(this.props.records).map(
              k =>
                <Record key={k}
                detail={this.props.records[k]} />
              )}
        </tbody>
      </table>
    );
  };
}

export default Read;

In its render() method, we build a table and render all the records row by row, using Javascript Object.keys() and map(). You should notice the curly brackets are used whenever the JavaScript ES6 is residing in HTML. As you notice, there is another component named Record used in this file. Let's create this extra component Record.js under src, and its content as followed:
import React from 'react';

class Record extends React.Component {
  detail = this.props.detail;

  render(){
    return(
      <tr>
        <td>{this.detail.begin}</td>
        <td>{this.detail.end}</td>
        <td>{this.detail.place}</td>
      </tr>
    );
  };
}

export default Record;

Therefore, the detail of a single travel record is passed from Read.js to Record.js and rendered in a table row. Now on the webbrowser, the travel records that you entered should be shown on the webapp. Furthermore, we can call the readRecord() method once a new record is created, so user do not need to refresh the page. Update the createRecord() in App.js
createRecord = (r) =>{
  const Travel = Parse.Object.extend('Travel');
  const record = new Travel();

  record.set('begin', r.begin);
  record.set('end', r.end);
  record.set('place', r.place);

  record.save().then(
    (result) => { this.readRecord() },
    (error) => { console.error(error) };
    }
  );
};
In next post, we will talk about Update and Delete of CRUD.

Comments

There is no comment yet

New Comment

Please Login to comment