CRUD application with localStorage in ReactJS

In the previous article of ReactJS, we saw how to create a React application. In this article, we will proceed from there and implement CRUD operation in that application. So let’s see a CRUD application with localStorage in ReactJS.

I am assuming you have gone through our last article and have initiated the following command to create a new React application.

npx create-react-app my-react-app

go to inside the directory,

cd my-react-app

and run,

npm start

Create a form

Its time to do some code, Now open the file src/App.js which is present inside the my-react-app folder. Currently, App.js is Functional Component convert it into Class component and add the following code.

import React from 'react';
import './App.css';
class App extends React.Component {
  constructor() {
    super();
    this.state = {
      data: [],
    };
  }
  submitForm = (e) => {
    e.preventDefault();
    const inputValue = e.target.input.value;
    this.setState((prevState) => ({ data: [...prevState.data, inputValue] }));
  };
  render() {
    return (
      <div className="app">
        <form onSubmit={(e) => this.submitForm(e)}>
          <input type="text" name="input" />
          <button type="submit">Add</button>
        </form>
      </div>
    );
  }
}
export default App;

In the above code, we have created a form with input and a button. And in the form tag, we have onSubmit function which gets triggered when the submit button is pressed. On submit we are calling a function “this.submitForm(e), in which we get the value from the input box and updating the state with this.setState.

Display list

Next is to display the data which is in the “this.state.data” array. Add the following after closing form the tag.

...  
<div className="data">
  <ul>
    {this.state.data.map((input, index) => (
      <li key={input + index}>{input}</li>
    ))}
  </ul>
</div>
...

Edit button and its functionality

After adding edit button to each list our code look like this.

import React from 'react';
import './App.css';
class App extends React.Component {
  constructor() {
    super();
    this.state = {
      data: [],
      isEditable: null,
    };
  }

  submitForm = (e) => {
    e.preventDefault();
    const inputValue = e.target.input.value;
    this.setState((prevState) => ({ data: [...prevState.data, inputValue] }));
  };

  edit = (index) => {
    this.setState({ isEditable: index });
  };

  updateForm = (e) => {};
  
  render() {
    const { data, isEditable } = this.state;
    return (
      <div className="app">
        <form onSubmit={(e) => this.submitForm(e)}>
          <input type="text" name="input" />
          <button type="submit">Add</button>
        </form>
        <div className="data">
          <ul>
            {data.map((input, index) => (
              <li key={input + index}>
                <div>
                  {input}
                  <button onClick={() => this.edit(index)}>Edit</button>
                </div>
                <div style={{ display: `${isEditable !== index ? 'none' : 'block'}` }}>
                  <form onSubmit={(e) => this.updateForm(e)}>
                    <input type="text" name="input" defaultValue={input} />
                    <button type="submit">update</button>
                  </form>
                </div>
              </li>
            ))}
          </ul>
        </div>
      </div>
    );
  }
}
export default App;

Oh! yes, It looks more than the just edit button. So I have implemented what happens when you click on the edit button, so I have added one more key in the state “isEditable”. So when you click on the edit button this.edit(index) function being called which accepts one parameter that is index position. This index position will replace the isEditable value every time you click on edit. Based on isEditable value I am deciding which update form should be displayed by this line of code style{{display:${isEditable!==index?'none':'block'}}}.

Update data functionality.

Let’s add update functionality, in the above code, we have a form for updating the data. In the input tag, we have already defaultValue which holds the previous value. You can also see there is an onSubmit event that has a function that is not complete. So lets complete that function and get over with this step. Add the following code in the updateForm function.

updateForm = (e) => {
  e.preventDefault();
  const inputValue = e.target.input.value;
  const { data, isEditable } = this.state;
  const newData = [...data]; //cloning state data
  newData[isEditable] = inputValue;
  this.setState({ data: newData });
};

Delete button and its functionality

Its time to do final CRUD operation which is delete. So add a delete button after update form (not in the form). This delete button will be having a onClick.

...
deleteData = () => {
  const { data, isEditable } = this.state;
  const newData = [...data]; //cloning state data
  const filteredData = newData.filter((input, index) => index !== isEditable);
  this.setState({ data: filteredData, isEditable: null });
};
...
<button type="button" onClick={this.deleteData}>
  Delete
</button>
...

I guess this block is well self explainable. When deleteData() function being called it filters the current array of data and returns the data which does not match with current isEditable value.

Add LocalStorage

The final step of our article is to add localstorage functionality. Now if you don’t know localStorage then you would be wondering what is the point of adding localStorage. I agree that now we have done add, edit, update, delete and everything is running smoothly but hang on, did you notice once you refresh the page all data you added is not there anymore. Yes, so we can fix this with the localStorage although you can also use the database. Learn more about localStorage here. Let’s see our final code after adding the localStorage feature.

import React from 'react';
import './App.css';
class App extends React.Component {
  constructor() {
    super();
    this.state = {
      data: [],
      isEditable: null,
    };
  }
  componentDidMount() {
    let check = window.localStorage.getItem('data');
    check = JSON.parse(check);
    if (Array.isArray(check) && check.length > 0) {
      this.setState({ data: check });
    }
  }
  submitForm = (e) => {
    e.preventDefault();
    const inputValue = e.target.input.value;
    this.setState(
      (prevState) => ({ data: [...prevState.data, inputValue] }),
      () => {
        window.localStorage.setItem('data', JSON.stringify(this.state.data));
      }
    );
  };
  edit = (index) => {
    this.setState({ isEditable: index });
  };

  updateForm = (e) => {
    e.preventDefault();
    const inputValue = e.target.input.value;
    const { data, isEditable } = this.state;
    const newData = [...data]; //cloning state data
    newData[isEditable] = inputValue;
    this.setState({ data: newData }, () => {
      window.localStorage.setItem('data', JSON.stringify(this.state.data));
    });
  };

  deleteData = () => {
    const { data, isEditable } = this.state;
    const newData = [...data]; //cloning state data
    const filteredData = newData.filter((input, index) => index !== isEditable);
    this.setState({ data: filteredData, isEditable: null }, () => {
      window.localStorage.setItem('data', JSON.stringify(this.state.data));
    });
  };

  render() {
    const { data, isEditable } = this.state;
    return (
      <div className="app">
        <form onSubmit={(e) => this.submitForm(e)}>
          <input type="text" name="input" />
          <button type="submit">Add</button>
        </form>
        <div className="data">
          <ul>
            {data.map((input, index) => (
              <li key={input + index}>
                <div>
                  {input}
                  <button onClick={() => this.edit(index)}>Edit</button>
                </div>
                <div
                  style={{
                    display: `${isEditable !== index ? 'none' : 'block'}`,
                  }}
                >
                  <form onSubmit={(e) => this.updateForm(e)}>
                    <input type="text" name="input" />
                    <button type="submit">update</button>
                  </form>
                  <button type="button" onClick={this.deleteData}>
                    Delete
                  </button>
                </div>
              </li>
            ))}
          </ul>
        </div>
      </div>
    );
  }
}
export default App;

This is all code with localStorage. There are a few things that need some explanation. So you see on every this.setState() , we are calling callback function and in that callback function we are adding state data into the localStorage using setItem() function. The setItem() accepts two parameters first one is the key and the second one is value.

There is the “componentDidMount()” function a React life cycle method that executes only once when component DOM mounts. In componentDidMount, we are calling getItem(‘data’) from localStorage property. In getItem(‘data’) data is the key name we used in this.setState() callback function to add state data.

Here is a question for you window.localStorage.setItem('data', JSON.stringify(this.state.data)); why is this code in the callback function of setState(), Why not in the next line?

Leave your answers in the comment section or if you don’t know please do little research this is important for you as react developer.

That’s all about CRUD application with localStorage in ReactJS. I hope this article has helped you. If you have any suggestions or doubt leave your comment in below comment section.

Github repository of this code can be found here

See also

Leave a Reply

Your email address will not be published. Required fields are marked *