1

I am trying to learn React Hooks with a Bootstrap Modal example. I have the following Modal (from the react bootstrap website).

import React, { useState } from "react";
import {Button, Modal} from 'react-bootstrap';

function SimpleModal() {

const [show, setShow] = useState(false);

const handleClose = () => setShow(false);
const handleShow = () => setShow(true);

return (
  <>
    <Button variant="primary" onClick={handleShow}>
      Launch demo modal
    </Button>

    <Modal show={show} onHide={handleClose} animation={true}>
      <Modal.Header closeButton>
        <Modal.Title>Modal heading</Modal.Title>
      </Modal.Header>
      <Modal.Body>Woohoo, you're reading this text in a modal!</Modal.Body>
      <Modal.Footer>
        <Button variant="secondary" onClick={handleClose}>
          Close
        </Button>
        <Button variant="primary" onClick={handleClose}>
          Save Changes
        </Button>
      </Modal.Footer>
    </Modal>
  </>
);
}
export default SimpleModal;

Inside an axios response I want to show this Modal

 import React, { Component } from 'react';
 import axios from 'axios';

 import SimpleModal from "../components/SimpleModal"

 export default class CreateInnovation extends Component {


constructor(props) {
    super(props);
    this.onChangeOwnerName = this.onChangeOwnerName.bind(this);
    this.onChangeOwnerDepartment = this.onChangeOwnerDepartment.bind(this);
    this.onChangeOwnerWorkLocation = this.onChangeOwnerWorkLocation.bind(this);
    this.onSubmit = this.onSubmit.bind(this);

    this.state = {
        owner_name: '',
        owner_department: '',
        owner_work_location: ''
    }
}

onChangeOwnerName(e) {
    this.setState({
        owner_name: e.target.value
    });
}
onChangeOwnerDepartment(e) {
    this.setState({
        owner_department: e.target.value
    });
}
onChangeOwnerWorkLocation(e) {
    this.setState({
        owner_work_location: e.target.value
    });
}


onSubmit(e) {
    e.preventDefault();
    
    console.log(`Form submitted:`);
    console.log(`Owner Name: ${this.state.owner_name}`);
    console.log(`Owner Department: ${this.state.owner_department}`);
    console.log(`Owner Work Location: ${this.state.owner_work_location}`);

    const newInnovation = {
        owner_name: this.state.owner_name,
        owner_department: this.state.owner_department,
        owner_work_location: this.state.owner_work_location
    };

    axios.post('http://localhost:4000/innov8/create', newInnovation)
        .then(res => {
                if (res.status === 200) {
                    console.log(res.data);
                    <SimpleModal show={"true"} />
                }
            }
         );
    // Purge state
    this.setState({
        owner_name: '',
        owner_department: '',
        owner_work_location: ''
    })
}

render() {
    return (
        <div>
            <h1 id="heading" className='margin'>Innovation<span className="badge badge-primary margin">A3</span></h1>
            <h5 className='margin'>Your Details</h5>

            <form id='Innovationform' onSubmit={this.onSubmit}>
                <div className="container-fluid">
                    <div className="form-row">
                        <div className="col-sm ">
                            <input 
                                id="owner"
                                type="text" 
                                className="form-control" 
                                value={this.state.owner_name}
                                onChange={this.onChangeOwnerName}
                                maxLength="40" 
                                placeholder="Owner Name" 
                                required
                                />
                        </div>
                        <div className="col-sm">
                            <select id="department" className='form-control' value={this.state.owner_department} onChange={this.onChangeOwnerDepartment} required>
                                <option value="" disabled>Select Your Department</option>
                                <option value="acc">Accounts</option>
                                <option value="cat">Catering</option>
                                <option value="com">Commercial</option>
                                <option value="dig">Digitalisation &amp; Business Transformation</option>
                                <option value="dri">Drilling</option>
                                <option value="ele">Electrical</option>
                                <option value="eng">Engineering</option>
                                <option value="fac">Facilities</option>
                                <option value="fin">Finance</option>
                                <option value="hse">HSE</option>
                                <option value="hum">Human Resources</option>
                                <option value="inf">Information Technology</option>
                                <option value="mar">Marine</option> 
                                <option value="mec">Mechanical</option>                
                                <option value="ops">Operations</option>
                                <option value="pay">Payroll</option>                      
                                <option value="pro">Procurement</option>
                                <option value="sub">Subsea</option>
                                <option value="tec">Technical</option>
                                <option value="war">Warehouse</option>   
                                <option value="oth">Other</option>                                  
                            </select>
                        </div>  
                        <div className="col-sm ">
                            <select id="workplace" className='form-control' value={this.state.owner_work_location} onChange={this.onChangeOwnerWorkLocation} required>
                                    <option value="" disabled>Select Your Workplace</option>
                                    <option value="abe">Aberdeen</option>
                                    <option value="car">Stena Carron</option>
                                    <option value="don">Stena Don</option>
                                    <option value="dri">Stena DrillMAX</option>
                                    <option value="for">Stena Forth</option>
                                    <option value="ice">Stena IceMAX</option>
                                    <option value="spe">Stena Spey</option>
                                    <option value="oth">Other</option>
                                </select>
                        </div>
                    </div>                          
                </div>
                <div className="container">
                    <div className="form-row">
                        <div className="col text-center">
                            <button id="formSubmit" type="submit" className="btn btn-primary center-block buttonload">Submit Your Innovation!</button>
                        </div>
                    </div>
                </div>
            </form>
        </div>
    )
}
 }

My thinking is that I need to create the SimpleModal component and use the setShow hook to set "true". I have been searching for a few hours now & am struggling a tad. I have a button in the Modal function just now for testing, but I want to remove this button and only open the modal on success of the Axios response. Am I looking in the right direction?

Colin Dawson
  • 133
  • 3
  • 16

2 Answers2

1

Here is the solution You have to pass show in props to the model

Put this state into your I'm guessing createOwner component from which you shared onSubmit method

this.state= {
  show: false,
  owner_name: '',
  owner_department: '',
  owner_work_location: ''
}

Pass is down to the SimpleModal component in which you are rendering modal

Render modal in your create owner component.

<SimpleModal show={this.state.show} onClose={()=>{this.setState({show:false})}} />

In Your simpleModel File get it from props

import React, { useState } from 'react'
import { Button, Modal } from 'react-bootstrap'
function SimpleModal({ show, onClose }) {
return (
    <>
        <Modal show={show} onHide={onClose} animation={true}>
            <Modal.Header closeButton>
                <Modal.Title>Modal heading</Modal.Title>
            </Modal.Header>
            <Modal.Body>Woohoo, you're reading this text in a modal!</Modal.Body>
            <Modal.Footer>
                <Button variant="secondary" onClick={onClose}>
                    Close
                </Button>
                <Button variant="primary" onClick={onClose}>
                    Save Changes
                </Button>
            </Modal.Footer>
        </Modal>
    </>
)
}
export default SimpleModal
Sangam Rajpara
  • 652
  • 3
  • 12
  • Thanks Sangam, I can't add const [show, setShow] = useState(false) to my create owner component as it is a class & I get this error: React Hook "useState" cannot be called in a class component. React Hooks must be called in a React function component or a custom React Hook function – Colin Dawson Jan 30 '21 at 21:02
  • I have added my full class so that you can see – Colin Dawson Jan 30 '21 at 21:05
  • Edited my answer. And Please don't mix class and functional components. Do upvote – Sangam Rajpara Jan 30 '21 at 21:17
  • Can't seem to get it to work. I want to access the handleShow function inside the CreateInnovation class rather than inside the SimpleModal. I can do it with passing the state, but not with the hook. – Colin Dawson Jan 30 '21 at 21:46
  • I also can't set 'show' inside the SimpleModal as it already exists in const [show, setShow] = useState(false); – Colin Dawson Jan 30 '21 at 21:50
  • https://reactjs.org/docs/lifting-state-up.html Read this once Now I can tell you that you have to lift `show` state from `SimpleModal` functional component to your `CreateInnovation` class – Sangam Rajpara Jan 30 '21 at 21:51
  • const [show, setShow] = useState(false); Remove this from your local component and keep this in parent component pass it down using props so from parent component(CreateInnovation class) you can close open modal. – Sangam Rajpara Jan 30 '21 at 21:53
  • Thanks Sangam, can you you update your answer with the SimpleModal function & I can mark as correct? – Colin Dawson Jan 30 '21 at 21:58
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/228061/discussion-between-sangam-rajpara-and-colin-dawson). – Sangam Rajpara Jan 30 '21 at 22:05
1

Based on your post, I assume you don't actually need that button that pops the modal up. Instead you only want to render it based on that axios call. If the call is successfully (i.e. returns the 200 code), you want to show the modal.

To do that, I would, in fact, create a separate element with the modal code like you do, but I would control its state from your main class. See, you can't render something from outside of the render function, so in your axios request function all you can do is change a state, which will signal to the class that a state has changed and a re-render is in order. So what we will do is set a state variable called show_modal to true when the axios call is good. Then in the render function, render the Modal element. Here is a Sandbox for you, but I will include the code here for future reference:

import React, { useState, Component } from "react";
import Modal from "react-bootstrap/Modal";
import Button from "react-bootstrap/Button";
import "bootstrap/dist/css/bootstrap.css";
import axios from "axios";

function SimpleModal({ handleClose }) {
  return (
    <>
      <Modal show={true} onHide={handleClose} animation={true}>
        <Modal.Header closeButton>
          <Modal.Title>Modal heading</Modal.Title>
        </Modal.Header>
        <Modal.Body>Woohoo, you're reading this text in a modal!</Modal.Body>
        <Modal.Footer>
          <Button variant="secondary" onClick={handleClose}>
            Close
          </Button>
          <Button variant="primary" onClick={handleClose}>
            Save Changes
          </Button>
        </Modal.Footer>
      </Modal>
    </>
  );
}

export default class CreateInnovation extends Component {
  constructor(props) {
    super(props);

    this.state = {
      show_modal: false,
      owner_name: "",
      owner_department: "",
      owner_work_location: ""
    };
  }

  makeAxiosCall = () => {
    axios.post("https://httpstat.us/200", { somedata: "" }).then((res) => {
      if (res.status === 200) {
        console.log(res.data);
        this.setState({ show_modal: true });
      }
    });
  };

  handleModalClose = () => this.setState({ show_modal: false });

  render() {
    const { show_modal } = this.state;

    return (
      <div className="App">
        <h1>Hello CodeSandbox</h1>
        <h2>Start editing to see some magic happen!</h2>

        <button onClick={this.makeAxiosCall}>Make axios call</button>

        {show_modal && <SimpleModal handleClose={this.handleModalClose} />}
      </div>
    );
  }
}

I used https://httpstat.us/200 for the test axios call because it always returns 200.

codemonkey
  • 7,325
  • 5
  • 22
  • 36