skip to Main Content

I have the below function which calls show a modal dialog on click of edit button. How do I render the functional component UpdateModal from function showModal?

Here is the function:

const showModal = (user: IUser) => {
  setShowUpdateModal(true);
  return (
    <>
      <div>Hello</div>
      <UpdateModal
        user={user}
        showUpdateModal={showUpdateModal}
        setShowUpdateModal={
          (bool: boolean | ((prevState: boolean) => boolean)) => 
            setShowUpdateModal(bool)
        }
      />
    </>
  );
}

UpdateModal:

import React, { FC } from 'react';
import { Modal } from 'react-bootstrap';
import styles from "../../styles/user.module.css";
import UpdateUser from './update.user';
import { IUser } from '../../models/user';

type updateModalProps = {
  setShowUpdateModal: (open: boolean) => void;
  showUpdateModal: boolean,
  user: IUser
};

const UpdateModal: FC<updateModalProps> = ({
  setShowUpdateModal,
  showUpdateModal,
  user
}) => {
  console.log("showUpdateModal....", showUpdateModal);
  return (
    <Modal showUpdateModal={showUpdateModal}>
      <Modal.Dialog>
        <Modal.Header closeButton onClick={() => setShowUpdateModal(false)}>
          <Modal.Title className={`${styles.createText}`}>
            Create User Form
          </Modal.Title>
        </Modal.Header>
        <Modal.Body>
          <UpdateUser user={user} setShowUpdateModal={setShowUpdateModal} />
        </Modal.Body>
      </Modal.Dialog>
    </Modal>
  );
}

export default UpdateModal;

User.tsx:


import UpdateModal from './update-modal';

function UserContent() {
  const [showUpdateModal, setShowUpdateModal] = useState(false);

  const showModal = (user: IUser) => {
    setShowUpdateModal(true);
    return (
      <>
        <div>Hello</div>
        <UpdateModal
          user={user}
          showUpdateModal={showUpdateModal}
          setShowUpdateModal={
            (bool: boolean | ((prevState: boolean) => boolean)) =>
              setShowUpdateModal(bool)
          }
        />
      </>
    );
    // return (<><div>Hello</div> <UpdateModal user={user} showUpdateModal={showUpdateModal} setShowUpdateModal={(bool: boolean | ((prevState: boolean) => boolean)) => setShowUpdateModal(bool)} /></>);
  }

  return (
    <div>
      <Table responsive striped bordered hover variant="light">
        <thead>
          <tr>
                    
          </tr>
        </thead>
        <tbody>
          {data && data.map((user, i) => (
            <tr key={user._id}>
              ....
              <td>
                <Pen
                  color="black"
                  size={20}
                  onClick={() => { showModal(user); }}
                />
              </td>
            </tr>
          ))}
        </tbody>
      </Table>
    </div>
  );
}

// display Users
const User = () => {
  return (
    <UserContent />
  )
}
export default User;

2

Answers


  1. The issue is that you can’t return JSX from a callback function and expect React to know it needs to render it somewhere.

    The showModal callback can update the showUpdateModal state and the parent component will be rerendered with the updated state value. From here you just render the modal as part of the render return of the parent component.

    Update the showUpdateModal state to hold a reference to a specific user, or a null value. Pass this to the modal’s user prop. All the modal closing actions should set the showUpdateModal state back to null to close the model.

    Example:

    function UserContent() {
      const [showUpdateModal, setShowUpdateModal] = useState<IUser | null>(null);
    
      const showModal = (user: IUser) => {
        setShowUpdateModal(user);
      };
    
      ...
    
      return (
        <>
          <div>
            <Table responsive striped bordered hover variant="light">
              ...
              <Pen
                color="black"
                size={20}
                onClick={() => showModal(user)} // <-- toggles modal open
             />
              ...
            </Table>
          </div>
    
          <UpdateModal
            user={showUpdateModal} // user or null
            showUpdateModal={!!showUpdateModal} // <-- toggles modal open/close
            setShowUpdateModal={setShowUpdateModal}
          />
    
          ...
        </>
      );
    }
    
    type updateModalProps = {
      setShowUpdateModal: (user: IUser | null) => void;
      showUpdateModal: IUser | null;
      user: IUser;
    };
    
    const UpdateModal: FC<updateModalProps> = ({
      setShowUpdateModal,
      showUpdateModal,
      user
    }) => {
      return (
        <Modal showUpdateModal={showUpdateModal}>
          <Modal.Dialog>
            <Modal.Header closeButton onClick={() => setShowUpdateModal(null)}>
              <Modal.Title className={`${styles.createText}`}>
                Create User Form
              </Modal.Title>
            </Modal.Header>
            <Modal.Body>
              <UpdateUser user={user} setShowUpdateModal={setShowUpdateModal} />
            </Modal.Body>
          </Modal.Dialog>
        </Modal>
      );
    }
    
    Login or Signup to reply.
  2. I think you need to conditionally render the UpdateModal component in the returned value of UserContent. Conceptually this is the flow most often seen:

    const UpdateModal = (...) => {...}
    
    const UserContent = (...) => {
      const [showUpdateModal, setShowUpdateModal] = useState(false);
    
      return (
        <>
          {showUpdateModal ? <UpdateModal /> : null}
          ...
          <Pen color="black" size={20} onClick={() => { setShowUpdateModal(true); }} />
    
        </>
    
      )
    }
    

    This code will trigger rerender when clicking Pen because state changes. Then, because showUpdateModal is true, UpdateModal will be rendered. However this will require you to reorganize your code significantly.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search