Search code examples
reactjsreact-nativeweb-deploymentuse-statesetstate

React state updating but not showing while render


I'm studying about react, I've made a form to save data with react, I want to update the table again as data is saved, I've used setState for this purpose, The issue is state updating but not rendering, I've tried some ways to find the issue,

  1. by using console.log() it is found that the state is properly changing
  2. render function is properly working after changing state

here's my code

import React, { Component } from "react";
import Header from "../header";
import Table from "./table";
import Addbox from "./create__box";
import $ from "jquery";
import { Api__url } from "../../vars";

class index extends Component {
  constructor(props) {
    super(props);
    // binding functions
    this.getData = this.getData.bind(this);
    this.table = this.table.bind(this);
    this.show_create_box = this.show_create_box.bind(this);
    // using state
    this.state = {
      data: "Fetching Records....",
      create_box: "",
    };
  }

  getData() {
    $.ajax({
      type: "POST",
      url: `${Api__url}services/get`,
      dataType: "JSON",
      success: this.table,
    });
  }

  table(d) {
    this.setState({ data: <Table data={d} /> });
  }

  show_create_box() {
    this.setState({create_box: <Addbox top="15vh" again_get_data={this.getData}/>});
  }

  componentDidMount() {
    this.getData();
  }

  render() {
    return (
      <>
        <Header />
        <div className="container mt-5 px-5">
          <div className="border__box p-2 col">
            <div className="row m-0 px-3">
              <div className="col">
                <h2>Services in home page</h2>
              </div>
              <div className="col d-flex justify-content-end">
                <button
                  onClick={this.show_create_box}
                  className="btn btn-outline-success show__create__box"
                >
                  Add New Data
                </button>
              </div>
            </div>
            <div className="row m-0 py-2 px-4">{this.state.data}</div>
          </div>
        </div>
        {this.state.create_box}
      </>
    );
  }
}

export default index;

```

the code from the table file
```
import React, { Component } from "react";
import Updatebox from "./update__box";

class table extends Component {
  constructor(props) {
    super(props);
    this.show_update_box = this.show_update_box.bind(this);
    this.delete_record = this.delete_record.bind(this);
    this.state = { update_box: "" };

    // making data
    this.tbody = props.data.map((d, index) => {
      return (
        <tr className="w-100 row m-0 p-0" id={d.services_id}>
          <th className="col-1" scope="row">
            {index + 1}
          </th>
          <td className="col-3">{d.services_name}</td>
          <td className="col-4">{d.services_description}</td>
          <td className="col-2">{d.services_icon}</td>
          <td className="col-1">
            <button
              onClick={() => {
                this.show_update_box(index);
              }}
              className="btn btn-outline-success"
            >
              Update
            </button>
          </td>
          <td className="col-1">
            <button
              onClick={() => {
                this.delete_record(index);
              }}
              className="btn btn-outline-danger"
            >
              Delete
            </button>
          </td>
        </tr>
      );
    });
  }

  // Function to get close form instruction from another class
  changeStuff(d) {
    this.setState(d);
  }

  delete_record(id) {
    alert(id);
  }

  show_update_box(key) {
    this.setState({
      update_box: (
        <Updatebox
          data={this.props.data[key]}
          changeHandler={this.changeStuff.bind(this)}
        />
      ),
    });
  }

  render() {
    return (
      <>
        <table className="position-relative table table-hover ">
          <thead className="table-dark">
            <tr className="row m-0 p-0">
              <th className="col-1">Sr #</th>
              <th className="col-3">Name</th>
              <th className="col-4">Description</th>
              <th className="col-2">Icon</th>
              <th className="col-1">Update</th>
              <th className="col-1">Delete</th>
            </tr>
          </thead>
          <tbody>{this.tbody}</tbody>
        </table>
        {this.state.update_box}
      </>
    );
  }
}

export default table;

```

here's the code for creating the box
```
import React, { Component } from "react";
import CloseIcon from "@mui/icons-material/Close";
import $ from "jquery";
// import i from "./index";

export default class Addbox extends Component {
  constructor(props) {
    super(props);
    this.data = { name: "", description: "", icon: "" };
    this.state = this.data;
    this.change = this.change.bind(this);
    this.submit = this.submit.bind(this);
    this.changeCSS = this.changeCSS.bind(this);
    this.setCSS = this.setCSS.bind(this);
    this.close_create_box = this.close_create_box.bind(this);
  }

  close_create_box() {
    this.props.changeHandler({ create_box: "" });
  }

  change(e) {
    var name = e.target.name;
    var value = e.target.value;
    this.setState({
      [name]: value,
    });
  }

  changeCSS() {
    $("#create").removeClass("btn-outline-success");
    $("#create").addClass("btn-outline-dark");
    $("#create").prop("disabled", true);
    $("#create").text("Sending....");
  }

  setCSS() {
    $("#create").removeClass("btn-outline-dark");
    $("#create").addClass("btn-outline-success");
    $("#create").prop("disabled", false);
    $("#create").text("Submit");
  }

  submit(e) {
    e.preventDefault();
    $.ajax({
      type: "POST",
      url: "http://127.0.0.1:8000/api/services/create",
      data: this.state,
      dataType: "JSON",
      beforeSend: this.changeCSS,
      success: (e) => {
        alert(e.message);
        this.props.again_get_data();
        this.close_create_box();
      },
      error: (e) => {
        this.setCSS();
        alert(e.responseText);
      },
    });
  }

  render() {
    return (
      <form
        onSubmit={this.submit}
        className="add__box create__box"
        method="POST"
        style={{ top: this.props.top }}
      >
        <div className="mb-3 row">
          <div className="col-10">
            <h4>Add New Service</h4>
          </div>
          <div className="col-2">
            <CloseIcon className="close" onClick={this.close_create_box} />
          </div>
        </div>
        <div className="mb-3">
          <label htmlFor="create_name" className="form-label">
            Name
          </label>
          <input
            id="create_name"
            name="name"
            type="text"
            className="form-control"
            onChange={this.change}
            value={this.state.name}
            required
            minLength={10}
            maxLength={100}
          />
        </div>
        <div className="mb-3">
          <label htmlFor="create_desc" className="form-label">
            Description
          </label>
          <textarea
            id="create_desc"
            className="form-control"
            onChange={this.change}
            required
            value={this.state.description}
            minLength={20}
            name="description"
          ></textarea>
        </div>
        <div className="mb-3">
          <label htmlFor="create_icon" className="form-label">
            Icon
          </label>
          <input
            id="create_icon"
            type="text"
            className="form-control"
            onChange={this.change}
            value={this.state.icon}
            required
            name="icon"
          />
        </div>
        <div className="m-5 row my-0">
          <button id="create" type="submit" className="btn btn-outline-success">
            Submit
          </button>
        </div>
      </form>
    );
  }
}

```

Solution

  • as per react docs, UI components shouldn't be in state and it should only contain the minimal amount of data needed to represent your UI's state.

    so instead of adding a UI element in the state, you should just add a minimal amount of data in it

    Here's an example based on your code

    constructor(props) {
       ...
       this.state = {
         status: "loading",
         data: "Fetching Records....",
         create_box: "",
       };
    }
    
    table(d) {
      this.setState({ data: d, status: "table" });
    }
    
    renderData() {
       const d = this.state.data
       const status = this.state.status
       if (status == "loading") { return d }
       if (status == "table") { return <Table data={d} /> }
    }
    
    render() {
        return (
          <>
            ...
            <div className="row m-0 py-2 px-4">{renderData()}</div>
            ...
          </>
        );
    }