user2281858
user2281858

Reputation: 1997

react modal does not open with useState hook

I have a component which renders a react-modal, my problem is the react-modal does not open. modalFileNameOpen useState property does not set properly.

const [modalFileNameOpen, setmodalFileNameOpen] = useState(false)

const handleFileNameChangeClick = () => {
    console.log(modalFileNameOpen)    // set to false
    setmodalFileNameOpen(true)     // this does not set the `modalFileNameOpen` to true. 
    console.log(modalFileNameOpen)    // is still false
}

return (
<div className="container-fluid">
    <input type="button" className="btn" onClick={handleFileNameChangeClick} value="Rename File"></input>

    <ModalFileName modalFileNameOpen={modalFileNameOpen} />

  </div>
)

I have another component ModalFileName

import React, { useState } from 'react';
import Modal from 'react-modal';
Modal.setAppElement('#root');
const ModalFileName = ({ modalFileNameOpen }) => {
    const [modalIsOpen, setModalIsOpen] = useState(modalFileNameOpen)

    return (
        <Modal isOpen={modalIsOpen} onRequestClose={() => setModalIsOpen(false)}>
            <div>
                <h1>File Name Change</h1>
                <div>
                    <button onClick={() => setModalIsOpen(false)}>Close</button>

                </div>
            </div>
        </Modal>
    )
}

export default ModalFileName;

Upvotes: 1

Views: 5588

Answers (1)

pilchard
pilchard

Reputation: 12911

Here is a minimal example of what you seem to be trying to achieve.

It is a basic illustration of pulling state up to the parent and passing a handler function down to the child. In your code you were attempting to do this by declaring a new state in the child based on the parent's state which leads to unnecessary duplication and possible state conflicts down the road.

We declare our modal state and setter and a basic handler function:

const [openModal, setOpenModal] = useState(false);

  const toggleModal = () => {
    setOpenModal(!openModal);
  }

We then conditionally render our modal component based on the current state value. If openModal is false we render the button to open it, otherwise we render the Modal component and pass our handler function toggleModal to it as a prop.

// if openModal is false render the button, else render our modal
{!openModal ?
   <button type="button" onClick={toggleModal}>Open Modal</button>
   : <Modal handler={toggleModal} />
}

If the modal is rendered it recieves the handler (here retrieved through destructuring function Modal({handler}) {...) and assign it to the onClick of the close button on our modal.

function Modal({handler}) {

  return (
    <div className="modal" >
      <p>I'm a modal</p>
      <button type="button" onClick={handler}>Close Modal</button>
    </div>
  )
}

Working Example

body {
  padding: 0;
  margin: 0;
}

.container {
  position: relative;
  height: 100vh;
  width: 100vw;
  background: gray;
}

.modal {
  height: 50vh;
  width: 50vw;
  position: absolute;
  top: 25%;
  left: 50%;
  transform: translateX(-50%);
  background-color: aqua;
 }
<script src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>

<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.26.0/babel.min.js"></script>
<div id="App"></div>
<script type="text/babel">
  const {useState} = React;

function App() {
  const [openModal, setOpenModal] = useState(false);

  const toggleModal = () => {
    setOpenModal(!openModal);
  }

  return (
    <div className="container">
      {!openModal ?
      <button type="button" onClick={toggleModal}>Open Modal</button>
      : <Modal handler={toggleModal} />
      }
    </div>
  )
}

function Modal({handler}) {

  return (
    <div className="modal" >
      <p>I'm a modal</p>
      <button type="button" onClick={handler}>Close Modal</button>
    </div>
  )
}

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


Some notes

If you console.log() before and after the setOpenModal() call you will get the same results that you did in your question; it will print the same value that it entered the render with. If it doesn't there is a problem and you have mutated the state. From the Docs

Never mutate this.state directly, as calling setState() afterwards may replace the mutation you made. Treat this.state as if it were immutable.

const [openModal, setOpenModal] = useState(false);

  const toggleModal = () => {
    console.log(openModal);   // prints the value of openModal on entering render
    setOpenModal(!openModal); // sets openModal to !openModal
    console.log(openModal);   // prints the value of openModal on entering render 
  }

Also, you may want to pass arguments to your passed handler (arguably the close button should always set openModal to false rather than hoping that toggling it will close it).

To do this you can simply accept an argument in your handler function.

const toggleModal = (newState) => {
    setOpenModal(newState);
  }

and pass it in your onClick calls.

<button type="button" onClick={() => toggleModal(true)}>Open Modal</button>
function Modal({handler}) {

  return (
    <div className="modal" >
      <p>I'm a modal</p>
        <button type="button" onClick={() => handler(false)}>Close Modal</button>
    </div>
  )
}

Upvotes: 1

Related Questions