反应:以编程方式打开模态(并自动清理)

时间:2018-08-02 21:49:52

标签: javascript reactjs jsx

我有一个站点,其中包含许多可以从任何地方打开的模式(例如LoginModal)。我遇到的挑战是,如果我使用ReactDOM.render之类的程序打开一个程序,那么在卸载父组件而不将其(以及所有可能的模态)放到模板中的情况下,如何自动清理它。

例如,打开它的方法如下:

openLoginModal() {
  ReactDOM.render(<LoginModal />, document.body);
}

LoginModal可以在关闭时自行清理。但是,如果从打开它的组件中删除了DOM,我该如何让LoginModal也知道要卸载。

我曾经想到的是使用Rx.Subject通知何时卸载,但这听起来像是一种错误的方法以及可能的反模式。

例如:

// modules/User.js
openLoginModal(unmountSubj) {
  const container = document.createElement('div');
  ReactDOM.render(<LoginModal />, container);
  unmountSubj.subscribe(() => {
    ReactDOM.unmountComponentAtNode(container);
  });
}

// components/RandomView.jsx
unmountSubject = new Rx.Subject();
componentWillUnmount() {
  this.unmountSubject.next();
}
login() {
  User.openLoginModal(this.unmountSubject);
}

我想避免在每个可能使用的JSX模板中包含所有可能的模态组件。

您将如何处理?

2 个答案:

答案 0 :(得分:0)

在我当前的React项目中,我已经通过使用多层组件架构解决了这个问题。

<App>
    // the standard starting point for React apps
    <DataLayer>
        // this is where I make API calls for data that is shared between all components
        <DisplayLayer>
            // this is where I put the methods to launch display elements that are shared
            // by all components (e.g., modals, alerts, notifications, etc.)
            <Template>
                // this is the first layer that is actually outputting HTML content
                <ModuleX>
                <ModuleY>
                <ModuleZ>
                    // these modules control the main display area of the screen, they encompass
                    // major UI functions (e.g., UsersModule, TeamsModule, etc.)
                    // when one of these modules needs to launch a shared UI element (like a 
                    // modal), they call a method in the <DisplayLayer> template - this means 
                    // that if I have a commonly-used modal (like, LoginModal), it doesn't need 
                    // to be included in every one of the core modules where someone might need 
                    // to initiate a login; these modules are mounted-and-unmounted as the user 
                    // navigates through the app  

因此,当应用程序加载时,<App><DataLayer><DisplayLayer><Template>都会全部加载(它们只会加载一次)。当用户四处浏览时,<ModuleX/Y/Z/etc>组件已安装和卸载,但是所有“常见内容”都保留在应用程序较高层中已安装/加载的位置。

答案 1 :(得分:0)

到目前为止,这是我想出的解决方案:这里有一个模式管理器模块,它将模块(通过ReactDOM.render呈现到DOM中,并返回一个将其卸载的函数。

这是简化版:

// modalManager.js
export default modalManager = {
  openModal(modalClass, props) {
    // Create container for the modal to be rendered into
    const renderContainer = document.createElement('div');
    document.body.appendChild(renderContainer);

    // Create & render component
    const modalInst = React.createElement(modalClass, { ...props, renderContainer });
    ReactDOM.render(modalInst, renderContainer);

    // Return unmounting function
    return () => this.unmountModal(renderContainer);
  },

  unmountModal(renderContainer) {
    ReactDOM.unmountComponentAtNode(renderContainer);
    renderContainer.parentNode.removeChild(renderContainer);
  },
}

// TestThing.jsx
class TestThing extends React.Component {

  unmountLogin = null;

  componentWillUnmount() {
    this.unmountLogin();
  }

  login() {
    this.unmountLogin = modalManager.openModal(Login, {});
  }
}

您还将注意到renderContainer被传递给模式组件。这样,模态可以在关闭时调用modalManager.unmountModal本身。

让我知道您的想法。