简体   繁体   中英

React: How can you pass a “not defined” variable as a prop to a component?

I'm new to reactjs and this may be a basic knowledge, but I cannot find any related information on the internet.

I have the following code that pops up a Modal window when you click some button (clicking the button results in history.push(url) action, which pops up the modal window ):



const ProjectBoard = () => {
  const match = useRouteMatch();
  const history = useHistory();
  const [filters, mergeFilters] = useMergeState(defaultFilters);

  const [IssueCreateModalOpen, setIssueCreateModalOpen] = useState(false);

  const [{ data, error, setLocalData }, fetchProject] = useApi.get('/project');

  if (!data) return <PageLoader />;
  if (error) return <PageError />;

  const { project } = data;

  const updateLocalProjectIssues = (issueId, updatedFields) => {
    setLocalData(currentData => ({
      project: {
        ...currentData.project,
        issues: updateArrayItemById(currentData.project.issues, issueId, updatedFields),
      },
    }));
  };
  
  return (
      <Fragment>
      <Header/>
      <Lists
        project={project}
        filters={filters}
        updateLocalProjectIssues={updateLocalProjectIssues}
      />

      <br/>

      <Route
        path={`${match.path}/issues/:issueId`}
        render={routeProps => (
          <Modal
            isOpen  // confusion 1: this variable is not defined anywhere!! 
            testid="modal:issue-details"
            width={1040}
            withCloseIcon={false}
            onClose={()=>history.push(match.url)}
            renderContent={modal => (
              <IssueDetails
                issueId={routeProps.match.params.issueId}
                trigger={routeProps.location.state.trigger}
                projectUsers={project.users}
                fetchProject={fetchProject}
                updateLocalProjectIssues={updateLocalProjectIssues}
                modalClose={modal.close}
              />
            )}
          />
        )}
      />
    </Fragment>

  );

}


export default ProjectBoard;


Below is where the Modal component is defined:

import React, { Fragment, useState, useRef, useEffect, useCallback } from 'react';
import ReactDOM from 'react-dom';
import PropTypes from 'prop-types';

import useOnOutsideClick from '../../hooks/onOutsideClick';
import useOnEscapeKeyDown from '../../hooks/onEscapeKeyDown';


const propTypes = {
  className: PropTypes.string,
  testid: PropTypes.string,
  variant: PropTypes.oneOf(['center', 'aside']),
  width: PropTypes.number,
  withCloseIcon: PropTypes.bool,
  isOpen: PropTypes.bool,
  onClose: PropTypes.func,
  renderLink: PropTypes.func,
  renderContent: PropTypes.func.isRequired,
};

const defaultProps = {
  className: undefined,
  testid: 'modal',
  variant: 'center',
  width: 600,
  withCloseIcon: true,
  isOpen: undefined,
  onClose: () => {},
  renderLink: () => {},
};

const Modal = ({
  className,
  testid,
  variant,
  width,
  withCloseIcon,
  isOpen: propsIsOpen,  // confusion 3: what does it mean, x:y ? 
  onClose: tellParentToClose,
  renderLink,
  renderContent,
}) => {
  console.log('---- propsIsOpen: ', propsIsOpen, typeof(propsIsOpen))
  const [stateIsOpen, setStateOpen] = useState(false);
  const isControlled = typeof propsIsOpen === 'boolean';
  const isOpen = isControlled ? propsIsOpen : stateIsOpen; // confusion 2: if isOpen is defined here, why even bother to pass a prop named as isOpen ?? 
  
  const $modalRef = useRef();
  const $clickableOverlayRef = useRef();

  const closeModal = useCallback(() => {
    if (!isControlled) {
      setStateOpen(false);
    } else {
      tellParentToClose();
    }
  }, [isControlled, tellParentToClose]);

  useOnOutsideClick($modalRef, isOpen, closeModal, $clickableOverlayRef);
  useOnEscapeKeyDown(isOpen, closeModal);

  useEffect(() => {

    console.log('Modal renderContent: ', renderContent)

    document.body.style.overflow = 'hidden';

    return () => {
      document.body.style.overflow = 'visible';
    };
  }, [isOpen]);

  return (
    <Fragment>
      {!isControlled && renderLink({ open: () => setStateOpen(true) })}

      {isOpen &&
        ReactDOM.createPortal(
          <ScrollOverlay>
            <ClickableOverlay variant={variant} ref={$clickableOverlayRef}>
                ... some code ...
            </ClickableOverlay>
          </ScrollOverlay>,
          $root,
        )}
    </Fragment>
  );
};

const $root = document.getElementById('root');

Modal.propTypes = propTypes;
Modal.defaultProps = defaultProps;

export default Modal;

The above code segments works for sure. I just cannot get my head around with:

confusion 1:the variable isOpen is not defined anywhere before it is pass as a prop to the Modal component

confusion 2: isOpen is defined within the Modal component as a "fresh new variable", why even bother to pass a prop named as isOpen to the Modal component in the first place??

confusion 3: what does x:y mean in the input for a component? ie isOpen: propsIsOpen,

In the place you've flagged "confusion1", isOpen isn't a variable, it's a property name. Using just a property name without a value (just isOpen , not isOpen="..." or isOpen={...} ) means it's a boolean property (like checked on HTML checkboxes). If you specify it, the value of the property is true . If you don't specify it, the value of the property is undefined (as always with unspecified properties).

Your confusion 2 and 3 are the same confusion: When destructuring, x: y means "take the property named x but put it in a variable/constant called y instead. (Basically, renaming it.) So the destructuring in Modal is copying the isOpen property it received into a parameter called propsIsOpen . That way, the code in the component can declare an isOpen variable with a slightly-adjusted value.

Here's an example of a boolean property:

 function Example({theProp}) { return <div> <code>typeof theProp = {typeof theProp}, theProp = {JSON.stringify(theProp)}</code> </div>; } ReactDOM.render( <div> <div><code>&lt;Example theProp/&gt;</code> :</div> <Example theProp /> <div><code>&lt;Example /&gt;</code> :</div> <Example /> </div>, document.getElementById("root") );
 <div id="root"></div> <script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.development.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.development.js"></script>

Here's an example of renaming destructuring:

 const obj = { b: 42, }; const {b: a} = obj; console.log(a);

Confusion 1:

<Modal
 isOpen  // confusion 1: this variable is not defined anywhere!! 
 testid="modal:issue-details"

Anything that's not defined or explicitly assigned value is boolean and it takes the default value as "true". So here you are passing a prop named isOpen as true to the Modal component. You would specify as isOpen={false} otherwise.

Ref:- https://reactjs.org/docs/jsx-in-depth.html#props-default-to-true

Confusion 3:

isOpen: propsIsOpen,  // confusion 3: what does it mean, x:y ? 

You need to research ES6 destructuring . To understand it the easy way, lets see an example through named exports below

Let's say you have a function called "doThis" from "someModule"

you can import it as

import { doThis } from "someModule";
         or
import { doThis as doIt } from "someModule"

in a similar way, de-structuring allows you to name a predefined variable to a name of your choice.

That's what "isOpen: propsIsOpen" is all about. You want to refer to isOpen as propIsOpen for readability or clean code or whatever the developer's reason/ preference is.

Confusion 2:

confusion 2: if isOpen is defined here, why even bother to pass a prop named as isOpen ?? 

Well, they have decided to use another variable in the modal component to receive the incoming prop (isOpen) value in order to do some conditional checks. Unfortunately, they've used the same name isOpen, so it has caused confusion.

const isModalOpen = isControlled ? propsIsOpen : stateIsOpen;

Naming it to something else like isModalOpen could have helped you avoid confusion number 2.

// confusion 3 - isOpen: propsIsOpen means const propsIsOpen = isOpen

// confusion 2 - it's actually a bad practice to redeclare constants like this, so basically it creates a new variable based on isOpen from props, if it exists there, or stateIsOpen otherwise

//confusion 1 - its done to make component optionally controllable from parent - if you pass isOpen - your parent component can control if your modal isOpen, otherwise it's controlled from within the modal itself

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM