Search code examples
javascriptreactjsreactstrapreact-state

Control modal from parent - React hooks | Reactstrap


I have a problem with controlling reactstrap Modal from the parent. I have basic knowledge of passing data to a child element, but I don't know how when it is more complex, like this... I'm in depression because I can't get it working for a whole day, sorry for bad English.

This is part of the code that is in the parent hook, this should on click show Modal (change state of child modal):

  import SignUp from "components/modals/ModalSignup";

  <NavLink href="#pablo" onClick={(e) => e.preventDefault()}>
      <SignUp/>
      <i className="ni ni-spaceship padding-right"></i>
      Register
  </NavLink>

How I should control the state of the Modal element from the parent element? e.g. When someone presses the register button it changes the state of the children hook (Modal)

Bellow is a working child hook where is reactstrap Modal element

import React from "react";

// reactstrap components
import {
  Button,
  Card,
  CardHeader,
  CardBody,
  FormGroup,
  Form,
  Input,
  InputGroupAddon,
  InputGroupText,
  InputGroup,
  Modal,
} from "reactstrap";

// Core Components

function ModalSignup() {
  const [modalOpen, setModalOpen] = React.useState(false);
  const [emailFocus, setEmailFocus] = React.useState("");
  const [passwordFocus, setPasswordFocus] = React.useState("");
  const [passwordConfirmFocus, setPasswordConfirmFocus] = React.useState("");
  return (
      <Modal
        isOpen={modalOpen}
        toggle={() => setModalOpen(!modalOpen)}
        className="modal-dialog-centered modal-sm"
      >
        <div className="modal-body p-0">
          <Card className="bg-secondary shadow border-0 mb-0">
            <CardHeader className="bg-white pb-5">
              <div className="text-muted text-center mb-3">
                <small>Sign Up with</small>
              </div>
              <div className="btn-wrapper text-center">
                <Button
                  className="btn-neutral btn-icon"
                  color="default"
                  href="#pablo"
                  onClick={(e) => e.preventDefault()}
                >
                  <span className="btn-inner--icon">
                    <img
                      alt="..."
                      src={require("assets/img/icons/common/google.svg")}
                    ></img>
                  </span>{" "}
                  <span className="btn-inner--text">Google</span>
                </Button>{" "}
                <Button
                  className="btn-neutral btn-icon"
                  color="default"
                  href="#pablo"
                  onClick={(e) => e.preventDefault()}
                >
                  <span className="btn-inner--icon">
                    <img
                      alt="..."
                      src={require("assets/img/icons/common/github.svg")}
                    ></img>
                  </span>{" "}
                  <span className="btn-inner--text">Github</span>
                </Button>
              </div>
            </CardHeader>
            <CardBody className="px-lg-5 py-lg-5">
              <div className="text-center text-muted mb-4">
                <small>Or sign up here:</small>
              </div>
              <Form role="form">
                <FormGroup className={"mb-3 " + emailFocus}>
                  <InputGroup className="input-group-alternative">
                    <InputGroupAddon addonType="prepend">
                      <InputGroupText>
                        <i className="ni ni-email-83"></i>
                      </InputGroupText>
                    </InputGroupAddon>
                    <Input
                      placeholder="Email"
                      type="email"
                      onFocus={() => setEmailFocus("focused")}
                      onBlur={() => setEmailFocus("")}
                    ></Input>
                  </InputGroup>
                </FormGroup>
                <FormGroup className={passwordFocus}>
                  <InputGroup className="input-group-alternative">
                    <InputGroupAddon addonType="prepend">
                      <InputGroupText>
                        <i className="ni ni-lock-circle-open"></i>
                      </InputGroupText>
                    </InputGroupAddon>
                    <Input
                      placeholder="Password"
                      type="password"
                      onFocus={() => setPasswordFocus("focused")}
                      onBlur={() => setPasswordFocus("")}
                    ></Input>
                  </InputGroup>
                </FormGroup>
                <FormGroup className={passwordConfirmFocus}>
                  <InputGroup className="input-group-alternative">
                    <InputGroupAddon addonType="prepend">
                      <InputGroupText>
                        <i className="ni ni-lock-circle-open"></i>
                      </InputGroupText>
                    </InputGroupAddon>
                    <Input
                      placeholder="Confirm Password"
                      type="password"
                      onFocus={() => setPasswordConfirmFocus("focused")}
                      onBlur={() => setPasswordConfirmFocus("")}
                    ></Input>
                  </InputGroup>
                </FormGroup>
                <div className="text-center">
                  <Button className="my-4" color="primary" type="button">
                    Sign Up
                  </Button>
                </div>
              </Form>
            </CardBody>
          </Card>
        </div>
      </Modal>
  );
}

export default ModalSignup;

Solution

  • Don't fall into depression. Try writing your SignUp modal this way:

    import React, {Fragment} from 'react';
    
    // reactstrap components
    import {
      Button,
      Card,
      CardHeader,
      CardBody,
      FormGroup,
      Form,
      Input,
      InputGroupAddon,
      InputGroupText,
      InputGroup,
      Modal,
      NavLink //NavLink import
    } from "reactstrap";
    
    // Core Components
    
    function ModalSignup() {
      const [modalOpen, setModalOpen] = React.useState(false);
      const [emailFocus, setEmailFocus] = React.useState("");
      const [passwordFocus, setPasswordFocus] = React.useState("");
      const [passwordConfirmFocus, setPasswordConfirmFocus] = React.useState("");
    
      const toggle = () => setModalOpen(!modalOpen);
    
      return (
         <Fragment>
    
           <NavLink href="#pablo" onClick={toggle}>
             <i className="ni ni-spaceship padding-right"></i>
             Register
           </NavLink>
    
          <Modal
            isOpen={modalOpen}
            toggle={toggle}
            className="modal-dialog-centered modal-sm"
          >
            <div className="modal-body p-0">
              <Card className="bg-secondary shadow border-0 mb-0">
                <CardHeader className="bg-white pb-5">
                  <div className="text-muted text-center mb-3">
                    <small>Sign Up with</small>
                  </div>
                  <div className="btn-wrapper text-center">
                    <Button
                      className="btn-neutral btn-icon"
                      color="default"
                      href="#pablo"
                      onClick={(e) => e.preventDefault()}
                    >
                      <span className="btn-inner--icon">
                        <img
                          alt="..."
                          src={require("assets/img/icons/common/google.svg")}
                        ></img>
                      </span>{" "}
                      <span className="btn-inner--text">Google</span>
                    </Button>{" "}
                    <Button
                      className="btn-neutral btn-icon"
                      color="default"
                      href="#pablo"
                      onClick={(e) => e.preventDefault()}
                    >
                      <span className="btn-inner--icon">
                        <img
                          alt="..."
                          src={require("assets/img/icons/common/github.svg")}
                        ></img>
                      </span>{" "}
                      <span className="btn-inner--text">Github</span>
                    </Button>
                  </div>
                </CardHeader>
                <CardBody className="px-lg-5 py-lg-5">
                  <div className="text-center text-muted mb-4">
                    <small>Or sign up here:</small>
                  </div>
                  <Form role="form">
                    <FormGroup className={"mb-3 " + emailFocus}>
                      <InputGroup className="input-group-alternative">
                        <InputGroupAddon addonType="prepend">
                          <InputGroupText>
                            <i className="ni ni-email-83"></i>
                          </InputGroupText>
                        </InputGroupAddon>
                        <Input
                          placeholder="Email"
                          type="email"
                          onFocus={() => setEmailFocus("focused")}
                          onBlur={() => setEmailFocus("")}
                        ></Input>
                      </InputGroup>
                    </FormGroup>
                    <FormGroup className={passwordFocus}>
                      <InputGroup className="input-group-alternative">
                        <InputGroupAddon addonType="prepend">
                          <InputGroupText>
                            <i className="ni ni-lock-circle-open"></i>
                          </InputGroupText>
                        </InputGroupAddon>
                        <Input
                          placeholder="Password"
                          type="password"
                          onFocus={() => setPasswordFocus("focused")}
                          onBlur={() => setPasswordFocus("")}
                        ></Input>
                      </InputGroup>
                    </FormGroup>
                    <FormGroup className={passwordConfirmFocus}>
                      <InputGroup className="input-group-alternative">
                        <InputGroupAddon addonType="prepend">
                          <InputGroupText>
                            <i className="ni ni-lock-circle-open"></i>
                          </InputGroupText>
                        </InputGroupAddon>
                        <Input
                          placeholder="Confirm Password"
                          type="password"
                          onFocus={() => setPasswordConfirmFocus("focused")}
                          onBlur={() => setPasswordConfirmFocus("")}
                        ></Input>
                      </InputGroup>
                    </FormGroup>
                    <div className="text-center">
                      <Button className="my-4" color="primary" type="button">
                        Sign Up
                      </Button>
                    </div>
                  </Form>
                </CardBody>
              </Card>
            </div>
          </Modal>
          </Fragment>
      );
    }
    
    export default ModalSignup;
    
    

    Then, just render your modal SignUp like this <ModalSignup /> and it will show your register link without rendering the Modal first. Once clicked, the modal will appear.

    I have not tested this, but this should work.