How do i handle array in react CRUD app implementation

Viewed 1315

I am building a CRUD app that stores name, dob, year as elements of an array (items) in state. Each displayed item from the array has an update and delete button. I'm currently stuck on the edit functionality and I want to:

  1. Set the defaultValue of each update inputbox to the displayed value
  2. Replace the existing value in the items array with the updated value

Here is the code I have so far

/* when 'edit' button is clicked */

<form onSubmit={this.handleUpdate}>
    <input 
        className=""
        name="name"
        value={/* this section */}
        placeholder= "Celebrant's Name" 
        ref={name => this.name = name}
        required/> 
    <input 
        className=""
        type="number" 
        name="day"
        min="1" 
        max="31"
        ref={day => this.day = day}
        placeholder= "day"/>
    <input 
        className=""
        name="dob"
        type="month"/>

    <button type="submit">update</button>
    <button onClick={this.handleEditCancel}>cancel</button>
</form>

/*displays each item in the items Array */

this.state.items.map((item, key) => ( 
    <li key={key}>
        <span> {item.name} </span>
        <span> {item.day} </span>
        <span> {item.dob} </span>
        <button 
            className="btn btn-light"
            onClick={this.handleEdit} >edit</button>
        <button 
            className="btn btn-danger" 
            onClick={() => this.handleDelete(key)}>delete</button>
    </li>
))}

/* edit functions */

handleEdit(){
    this.setState({ toggle: true });
}

handleUpdate(event){
    event.preventDefault();
    console.log(this.name.value);
}

/* state */ 
this.state = {
    name: '',
    day: '',
    dob: '',
    toggle: false,
    items : []
}

How can I implement this?

4 Answers

You should use ID for all the input boxes. By using those IDs you can access the value through getElementById.value and you can you setState to update the state variable. Update the State through handleEdit method. Each row item should be maintained by a unique id. This has to be used to edit the selected data.

Some things that have helped me when making forms:

  • Save an identifier of the item I am editing
  • use the onChange and value attributes of the input tag to two way bind the form data to the state of the form.
  • don't even try to use the native Date library. https://date-fns.org/ and https://momentjs.com/ make things so easy and it's not worth the trouble

Here are some snippets that will hopefully help:

// use a state that includes the somewhere to hold the item being edited and the form values
this.state = { items, itemBeingEdited: -1, form: null };

...
// when you handleEdit set the itemBeingEdited and initialize the form state
handleEdit(key) {
    this.setState({ itemBeingEdited: key, form: this.state.items[key] });
}

...
// keep the form state updated as the items change
 handleOnChange(e) {
    this.setState({
      form: { ...this.state.form, [e.target.name]: e.target.value }
    });
  }

...
// update the cooresponding item and reset form state when item is updated
handleUpdate(event) {
    event.preventDefault();
    const { itemBeingEdited, form } = this.state;
    this.setState({
      items: items.map((item, key) =>
        key === itemBeingEdited
          ? { ...form, dob: moment(form.dob).toDate() }
          : item
      ),
      form: null,
      itemBeingEdited: -1
    });
  }

My complete solution can be found here: https://codesandbox.io/s/magical-shadow-v7vp5

You can try this-

import React from "react";

export default class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      items: [
        { name: "John", dob: "2020-03" },
        { name: "Key", dob: "2010-07" },
        { name: "Mo", dob: "2000-08" }
      ],
      currentItem: {},
      currentIndex: -1,
      editMode: false
    };
    this.dataChanged = this.dataChanged.bind(this);
    this.handleEdit = this.handleEdit.bind(this);
    this.handleDelete = this.handleDelete.bind(this);
    this.handleEditCancel = this.handleEditCancel.bind(this);
    this.handleUpdate = this.handleUpdate.bind(this);
  }
  handleEdit(index) {
    this.setState({
      currentIndex: index,
      editMode: true,
      currentItem: { ...this.state.items[index] }
    });
  }
  handleDelete(key) {
    this.setState({
      items: this.state.items.filter((a, i) => i !== key)
    });
  }
  handleEditCancel(e) {
    this.setState({
      currentIndex: -1,
      editMode: false,
      currentItem: {}
    });
    e.preventDefault();
  }
  handleUpdate() {
    let items = this.state.items;
    items[this.state.currentIndex] = { ...this.state.currentItem };
    this.setState({
      currentIndex: -1,
      editMode: false,
      currentItem: {},
      items: items
    });
  }
  dataChanged(event){
    let field = event.target.name;
    let currentItem = {...this.state.currentItem};
    currentItem[field] = event.target.value;
    this.setState({currentItem: {...currentItem}});
  }
  render() {
    return (
      <div className="App">
        <ul>
          {this.state.items.map((item, key) => (
            <li key={key}>
              <span> {item.name} </span>
              <span> {item.dob} </span>
              <button
                className="btn btn-light"
                onClick={() => this.handleEdit(key)}
              >
                edit
              </button>
              <button
                className="btn btn-danger"
                onClick={() => this.handleDelete(key)}
              >
                delete
              </button>
            </li>
          ))}
        </ul>
        {this.state.editMode && (
          <form onSubmit={this.handleUpdate}>
            <input
              className=""
              name="name"
              value={this.state.currentItem.name}
              onChange={this.dataChanged}
              placeholder="Celebrant's Name"
              ref={name => (this.name = name)}
              required
            />
            <input
              className=""
              name="dob"
              onChange={this.dataChanged}
              value={this.state.currentItem.dob}
              type="month"
            />

            <button type="submit">update</button>
            <button onClick={e => this.handleEditCancel(e)}>cancel</button>
          </form>
        )}
      </div>
    );
  }
}

https://codesandbox.io/s/react-crud-ujs4i

If I understand so you have a: * master component that contains a component take care of form * component that take care of list of items * component that take care of each item - will start with the itemComponent:

    import React, { Component } from "react";

class Item extends Component {
  state = { item: this.props.value };

  render() {
    // we suppose that item dont contains duplicate key
    var item = this.state.item;
    return (
      <li key={item.key}>
        <span> {item.name} </span>
        <span> {item.day} </span>
        <span> {item.dob} </span>
        <button
          className="btn btn-light"
          onClick={() => this.props.handleEdit(item)}
        >
          edit
        </button>
        <button
          className="btn btn-danger"
          onClick={() => this.props.handleDelete(item /*or item.key*/)}
        >
          delete
        </button>
      </li>
    );
  }
}

export default Item;

for the item-form componenet:

import React, { Component } from "react";

class ItemForm extends Component {
  state = {
    item: {
      day: "",
      name: "",
      dob: "",
      key: ""
    }
  };

  handleEditCancel(item) {
    console.log(item);
  }
  handleChange() {
    console.log("in handle change");
  }

  render() {
    var item = this.state.item;
    return (
      <form onSubmit={this.handleUpdate}>
        <input
          className=""
          name="name"
          value={/* this section */ item.name}
          placeholder="Celebrant's Name"
          //   ref={name => (this.name = name)}
          onChange={this.handleChange}
          required
        />
        <input
          className=""
          type="number"
          name="day"
          min="1"
          max="31"
          value={item.day}
          onChange={this.handleChange}
          //   ref={day => (this.day = day)}
          placeholder="day"
        />
        <input
          className=""
          name="dob"
          onChange={this.handleChange}
          type="month"
          value={item.dob}
        />

        <button type="submit">update</button>

        {/* <button onClick={() => this.props.handleEditCancel(this.state.option)}></button>  */}
        <button onClick={this.handleEditCancel}>cancel</button>
      </form>
    );
  }
}

export default ItemForm;

for the master components items:

import React, { Component } from "react";
import ItemForm from "./item-form";
import Item from "./item";

class Items extends Component {
  state = { items: [] };
  handleEditCancel(item) {
    // you can either put the edit in the master component or in child component but I prefer the master component take care of all his child delete add or modify any items
    console.log(item);
  }
  handleEdit(item) {
    console.log("handle edit", item);
  }
  handleDelete(item) {
    console.log("handle delete", item);
  }
  componentDidMount() {
    var items = [
      { Key: 1, day: 1, name: "name1", dob: "dob1" },
      { Key: 2, day: 2, name: "name2", dob: "dob2" },
      { Key: 3, day: 3, name: "name3", dob: "dob3" }
    ];
    this.setState({ items: items });
  }
  render() {
    // we suppose that the each item contain a property Key
    return (
      <div>
        <ItemForm
          handleEditCancel={this.handleEditCancel.bind(this)}
        ></ItemForm>
        <div>
          {this.state.items.map(item => (
            <Item
              key={item.Key}
              value={item}
              // add button here update and delete
              handleEdit={this.handleEdit.bind(this)}
              handleDelete={this.handleDelete.bind(this)}
            />
          ))}
        </div>
      </div>
    );
  }
}

export default Items;

after you can create another component for inputs and button. the ideas is that the master components should take care of everything, any update, add or delete action and pass the information to childs vs props. hope this help.

Related