0
votes

I am using React-bootstrap to display a delete confirmation modal

Here is my confirmation model

import React from "react";
import PropTypes from "prop-types";
import Button from "@material-ui/core/Button";
import Modal from "react-bootstrap/lib/Modal";
import "./ConfirmationModal.scss";

class ConfirmationModal extends React.PureComponent {
  render() {
    return (
      <Modal
        {...this.props}
        size="md"
        aria-labelledby="contained-modal-title-vcenter"
        centered
        className="confirmation-modal"
      >
        <Modal.Header closeButton />
        <Modal.Body>
          <h4>Are you sure you want to delete?</h4>
          <p>{`You are about to delete ${this.props.deleteItem}`}</p>
        </Modal.Body>
        <Modal.Footer>
          <Button className="cancel-btn btn" onClick={this.props.onHide}>
            {"No, Cancel"}
          </Button>
          <Button className="delete-btn btn" onClick={this.props.onDelete}>
            {"Yes, Delete"}
          </Button>
        </Modal.Footer>
      </Modal>
    );
  }
}

ConfirmationModal.propTypes = {
  onClick: PropTypes.func,
  onDelete: PropTypes.func,
  deleteItem: PropTypes.string
};

ConfirmationModal.defaultProps = {
  onClick: () => {},
  onDelete: () => {},
  deleteItem: null
};

export { ConfirmationModal as default };

and here is the page it is being called on (Ultimately, I will be adding the snippet to the GridCommandCell delete function but for simplicity I am creating the Button

import React from "react";
import PropTypes from "prop-types";
import Button from "@material-ui/core/Button";
import { Grid, GridColumn as Column } from "@progress/kendo-react-grid";
import GridLoader from "../../../common/utils/grid-loader";
import GridCommandCell from "../../../common/grid-command-cell";

import ConfirmationModal from "../../../common/confirmation-modal";
import "./UserGrid.scss";

class UserGrid extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      modalShow: false,
      gridData: { data: [], total: 0 },
      dataState: {
        take: 10,
        skip: 0,
        filter: [],
        sort: [
          {
            field: "Id",
            dir: "desc"
          }
        ]
      }
    };

    this.grid = React.createRef();
  }

  dataRecieved = gridData => {
    this.setState(prevState => ({
      ...prevState,
      gridData
    }));
  };

  dataStateChanged = e => {
    const index = e.data.filter.filters.findIndex(x => x.field === "Role.Name");

    if (index >= 0) e.data.filter.filters[index].ignoreCase = true;

    this.setState(prevState => ({
      ...prevState,
      dataState: e.data
    }));
  };

  refresh = () => {
    this.grid.refresh();
  };

  render() {
    const result = (
      <div className="gridContent-grid">
        <Grid
          id="user-grid"
          filterable
          sortable
          pageable={{
            buttonCount: 5,
            info: false,
            type: "numeric",
            pageSizes: false,
            previousNext: false
          }}
          {...this.state.dataState}
          {...this.state.gridData}
          onDataStateChange={this.dataStateChanged}
        >
          <Column field="Id" filter="numeric" title="Id" />
          <Column field="FirstName" title="First Name" />
          <Column field="LastName" title="Last Name" />
          <Column field="Role.Name" title="Role" />
          <Column
            className="command-btn"
            width="100%"
            cell={props => {
              const commandCell = (
                <GridCommandCell
                  onEdit={() => this.props.onEdit(props.dataItem.Id)}
                  // I need to set the onDelete to on click, activate modal
                  // onDelete={() => this.props.onDelete(props.dataItem.Id)}
                  onClick={() => this.setState({ modalShow: true })}
                />
              );
              return commandCell;
            }}
            filterable={false}
            sortable={false}
          />
          <ConfirmationModal
            show={this.state.modalShow}
            onHide={() => this.setState({ modalShow: false })}
            // onDelete={() => this.props.onDelete(dataItem.Id)}
            // deleteItem={`${dataItem.FirstName} ${dataItem.LastName}`}
          />
        </Grid>
        <GridLoader
          url="/odata/users?$select=Id, FirstName, LastName&$expand=Role&count=true&"
          dataState={this.state.dataState}
          onDataRecieved={this.dataRecieved}
          ref={grid => {
            this.grid = grid;
          }}
        />
        <Button
          variant="contained"
          className="delete-btn btn"
          type="button"
          onClick={() => {
            console.log(this.state);
            () => this.setState({ modalShow: true });
            console.log(this.state);
          }}
        >
          <span className="button-label">Delete modal</span>
        </Button>
      </div>
    );

    return result;
  }
}

UserGrid.propTypes = {
  onEdit: PropTypes.func,
  onDelete: PropTypes.func
};

UserGrid.defaultProps = {
  onEdit: () => {},
  onDelete: () => {}
};

export { UserGrid as default };

I am trying to update the modal's state via the onClick event as documented in React-bootstrap Modals

Via this button

       <Button
          variant="contained"
          className="delete-btn btn"
          type="button"
          onClick={() => {
            console.log(this.state);
            () => this.setState({ modalShow: true });
            console.log(this.state);
          }}
        >
          <span className="button-label">Delete modal</span>
        </Button>

However, my console.log statements are both returning modalShow: false

I cannot figure out why the state will not update after on the onClick event.

Please assist. Thank you!

1
I went through your previous questions and saw that you don't accept answers, please when someone answer your question with the correct answer. Accept the answer by clicking tick mark next to their answer. - TenTen Peter
@TenTenPeter thank you for the advice. I am not sure of the proper edict. Will be sure to go back and do that. - TylerYoc

1 Answers

1
votes

Set state is asynchronous.

setState() does not immediately mutate this.state but creates a pending state transition. Accessing this.state after calling this method can potentially return the existing value. There is no guarantee of synchronous operation of calls to setState and calls may be batched for performance gains.

So doing

this.setState({ modalShow: true });
// might give old values, as state hasn't been set yet.
console.log(this.state);

Your state doesn't update as you are doing () => this.setState({ modalShow: true }); change it to the following.

console.log(this.state);
this.setState({ modalShow: true }, console.log(this.state)); // pass setstate a callback which will be executed after the state is set.

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.0/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.21.1/babel.min.js"></script>
<div id="root"></div>

<script type="text/babel">

class App extends React.Component {
  constructor() {
    super();
    this.state = {
      name: 'React',
      modalShow: false
    };
  }

  render() {
    return (
      <div>
        <p onClick={() => {
          console.log(this.state);
          this.setState({ modalShow: true }, () => {
            console.log(this.state);
          });
          
        }}>
          Calling state with a callback
        </p>
        <p onClick={()=> {
          console.log(this.state);
          this.setState({ modalShow: true });
          console.log(this.state);
        }}>
        Priniting state after setting value
        </p>
      </div>
    );
  }
}

ReactDOM.render(<App />, document.getElementById('root'));
</script>