繁体   English   中英

React - 将 JSX / React 组件传递给“无关”组件

[英]React - Pass JSX / React component to "unrelated" component

我正在寻找一种灵活的方法,允许我将一些 JSX 发送到完全不相关的组件。 例如,模态。

假设我们有一些用于全局存储的样板代码,我们可以使用自定义useStore挂钩从任何组件访问它。 所以一个非常准系统的模式可能看起来像这样:

// Modal.jsx
function Modal() {
  const store = useStore()

  if(!store.modal.isVisible) return null

  return <div>{store.modal.content}</div>
}

现在,如果我只想显示一些文本,那完全没问题。 即使 JSX 不应该属于商店,当store.modal.content是一个 React 组件时它也可以工作。 但是,它在使用钩子时会中断。 以下将是语法方面的理想解决方案,但不适用于 mobx 作为商店。 就像我说的,我不想通过将 JSX 或useRef()引用放在商店中来冒一些奇怪行为的风险。

function ToggleThing() {
  const [isActive, setActive] = useState(false)

  return <button onClick={() => setActive(!isActive)}>{isActive.toString()}</button>
}


function SomeModalTrigger() {
  const store = useStore();

  return (
    <button
      onClick={() =>
       store.setModalContent(<ToggleThing />) // <-- This would be perfect
      }
    >
      Trigger Modal
    </button>
  );
}

非理想解

内容需要是真正动态的,因此仅保存字符串和商店中的一些道具并使用对象作为组件查找的解决方案是行不通的。


const modalComponents = {
  someComponent,
  anotherComponent
}
// -> modalComponents[store.modal.component]

另外,我想避免一些存储 JSX 并且不属于商店/挂钩/组件的悬挂变量。 尽管这可行,但它使维护变得困难并打破了 React 的核心概念

let modalContent = null // Not in a component

function Modal() {
  const store = useStore()

  return <div>{modalContent}</div> 
  // If modalContent is updated without triggering a component rerender, the content becomes stale
}

function SomeModalTrigger() {
  const store = useStore();

  return (
    <button
      onClick={() => {
        modalContent = <ToggleThing />;
        store.triggerModalUpdate();
      }}
    >
      Trigger Modal
    </button>
  );
}

注意:实际代码比这复杂得多,而且与模态无关,所以它不像使用模态包那么容易。 模态只是对我来说描述问题的最平易近人的方式。

你是对的; 将 JSX 元素放入类似商店的地方并不理想,因为它们不可序列化。

实现此目的的一种方法是使用createPortal ,它允许您在特定 DOM 节点下传送/渲染 JSX 元素(组件)。

如果您一次只需要显示一个模态,您可以给模态容器一个唯一且固定的id ,并使用SomeModalTrigger中的document.getElementById检索 DOM 节点,如官方文档中所示。

如果您需要同时显示多个模式,您可能需要为每个模式提供一个动态id (您可以使用新的useId挂钩生成),并将此 ID 存储在商店中,以便SomeModalTrigger可以访问它.

不确定商店,但您可以使用上下文 api。 看看下面。

请注意,我们将组件包装在一个匿名函数中,即使它本身是一个函数。 那是因为 useState 和 setState 允许我们传递一个采用先前状态的回调。 setCounter(prev => prev + 1)

因此,当它找到一个函数时,它会假定它是那个回调并调用它。 因此,为了避免它调用我们的 createElement 函数,我们传递了我们自己的返回组件的回调。

 const { useContext, useState, createContext } = React const ModalContext = createContext(); // Sample modal bodies const H1 = ({name}) => <h1>{name}</h1> const H2 = ({name}) => <h2>{name}</h2> const App = () => { const [Modal, setModal] = useState(() => H1); const [modalProps, setModalProps] = useState({name: 'Foo'}); const modalContext = {Modal, setModal, modalProps, setModalProps} return ( <div> <ModalContext.Provider value={modalContext}> <Page /> </ModalContext.Provider> </div> ) } const ModalWrapper = () => { const {Modal, modalProps} = useContext(ModalContext) return ( <div> <Modal {...modalProps} /> </div> ) } const Page = () => { const {setModal, setModalProps} = useContext(ModalContext); function setH1() { setModal(() => H1); setModalProps({name: 'Foo'}) } function setH2() { setModal(() => H2) setModalProps({name: 'Bar'}) } return ( <div> <button onClick={setH1}>Set H1</button> <button onClick={setH2}>Set H2</button> <ModalWrapper /> </div> ) } ReactDOM.render(<App />, app)
 <script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script> <script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script> <div id="app"></div>

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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