首页 > 解决方案 > React:如何将“未定义”变量作为道具传递给组件?

问题描述

我是 reactjs 新手,这可能是基础知识,但我在互联网上找不到任何相关信息。

我有以下代码,当您单击某个按钮时会弹出一个模态窗口(单击该按钮会导致history.push(url)动作,这会弹出模态窗口):



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;


下面是Modal定义组件的地方:

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;

上面的代码段肯定有效。我只是无法理解:

混淆1:变量isOpen在作为道具传递给Modal组件之前没有在任何地方定义

困惑2:在组件isOpen内被定义Modal为“新鲜的新变量”,为什么还要首先将名为 as 的道具传递isOpenModal组件?

x:y困惑3:组件的输入是什么意思?IEisOpen: propsIsOpen,

标签: javascriptreactjs

解决方案


在您标记“confusion1”的地方,isOpen不是变量,而是属性名称。仅使用不带值的属性名称(just isOpen、notisOpen="..."isOpen={...})意味着它是一个布尔属性(如checkedHTML 复选框)。如果您指定它,则该属性的值为true。如果您不指定它,则该属性的值是undefined(与未指定的属性一样)。

您的混淆 2 和 3 是相同的混淆:解构时,x: y意味着“获取命名的属性x,但将其放入一个名为的变量/常量中y。(基本上,重命名它。)因此,解构Modal是将其接收到的属性复制isOpen到参数中调用propsIsOpen. 这样,组件中的代码可以声明一个isOpen具有略微调整值的变量。

这是一个布尔属性的示例:

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>

这是重命名解构的示例:

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


推荐阅读