简体   繁体   English

在 React 高阶组件中包含模态功能

[英]Include Modal functionality in React Higher-Order Component

I created the below HOC which I can use to wrap a React component to add 2 inactivity timers: the first to show the user a warning;我创建了下面的HOC ,我可以用它来包装 React 组件以添加 2 个不活动计时器:第一个向用户显示警告; the other to log them out.另一个将它们注销。 I got the idea from here and it seems to work pretty well.我从这里得到了这个想法,它似乎工作得很好。 That is, I can add withTimer functionality to a component by wrapping it like this:也就是说,我可以通过像这样包装它来将withTimer功能添加到组件中:

export default withTimer(DragDropContext(HTML5Backend)(App));

The problem is the warning alert box halts the event loop (as alert boxes apparently always do), so the logout function is never reached.问题是警告警报框会停止事件循环(警报框显然总是这样做),因此永远不会达到注销 function。

I believe a modal (eg, from react-bootstrap) would solve this, as it presumably would not halt the event loop, thus the logout would occur as intended if the user is still idle after the warning alert.我相信模式(例如,来自 react-bootstrap)可以解决这个问题,因为它可能不会停止事件循环,因此如果用户在警告警报后仍然空闲,则会按预期进行注销。

How would I change the below HOC to use a modal for the warning instead of an alert box?我将如何更改下面的 HOC 以使用模式作为警告而不是警报框? Is this possible?这可能吗? That is, can a HOC that's used to wrap another component include a component itself (ie, the modal) so as to keep it decoupled from the wrapped component itself?也就是说,用于包装另一个组件的 HOC 是否可以包含组件本身(即模态),以使其与被包装的组件本身分离?

import React from 'react';
import { Modal } from 'react-bootstrap';

const withTimer = (WrappedComponent) => {
  class WithTimer extends React.Component {
    constructor(props) {
      super(props);
      this.state = {
        warningTime: 5000,
        signoutTime: 10000
      };

      this.events = [
        'load',
        'mousemove',
        'mousedown',
        'click',
        'scroll',
        'keypress'
      ];

      for (var i in this.events) {
        window.addEventListener(this.events[i], this.resetTimeout);
      }

      this.setTimeout();
    }

    clearTimeoutFunc = () => {
      if (this.warnTimeout) clearTimeout(this.warnTimeout);
      if (this.logoutTimeout) clearTimeout(this.logoutTimeout);
    };

    setTimeout = () => {
      this.warnTimeout = setTimeout(this.warn, this.state.warningTime);
      this.logoutTimeout = setTimeout(this.logout, this.state.signoutTime);
    };

    resetTimeout = () => {
      this.clearTimeoutFunc();
      this.setTimeout();
    };

    warn = () => {
      window.alert('You will be logged out soon. Click to stay logged in.');
    };

    logout = () => {
      window.alert('You are being logged out!');
      // log the user out here
    };

    render() {
      console.log('HOC');
      return <WrappedComponent {...this.props.children} />;
    }
  }
  return WithTimer;
};

export default withTimer;

If you wanted to use a Modal, you could do something like this:如果你想使用 Modal,你可以这样做:

Live Demo现场演示

withTimer.js withTimer.js

import React from 'react';
import MyModal from './MyModal';

const withTimer = (WrappedComponent) => {
  class WithTimer extends React.Component {
    constructor(props) {
      super(props);
      this.state = {
        warningTime: 5000,
        signoutTime: 10000,
        showModal: false,
        modalMessage: "",
        modalButtonText: "",
      };

      this.events = [
        'load',
        'mousemove',
        'mousedown',
        'click',
        'scroll',
        'keypress'
      ];

      for (var i in this.events) {
        window.addEventListener(this.events[i], this.resetTimeout);
      }

      this.setTimeout();
    }

    clearTimeoutFunc = () => {
      if (this.warnTimeout) clearTimeout(this.warnTimeout);
      if (this.logoutTimeout) clearTimeout(this.logoutTimeout);
    };

    setTimeout = () => {
      this.warnTimeout = setTimeout(this.warn, this.state.warningTime);
      this.logoutTimeout = setTimeout(this.logout, this.state.signoutTime);
    };

    resetTimeout = () => {
      this.clearTimeoutFunc();
      this.setTimeout();
    };

    onModalClick = () => {
      this.setState({
        showModal: false,
      }, () => this.resetTimeout())
    }

    warn = () => {
      this.setState({
        modalButtonText: "Stay Logged In",
        modalHeader: "Warning!",
        modalMessage: 'You will be logged out soon. Click to stay logged in.',
        showModal: true,
      });
    };

    logout = () => {
      this.setState({
        modalButtonText: "Ok",
        modalHeader: "Session Timed Out",
        modalMessage: 'You are being logged out!',
        showModal: true,
      });
      // log the user out here
    };

    render() {
      console.log('HOC');
      return (
        <>
        <MyModal 
          show={this.state.showModal} 
          modalMessage={this.state.modalMessage}
          modalHeader={this.state.modalHeader}
          buttonText={this.state.modalButtonText}
          onButtonClick={this.onModalClick} />
        <WrappedComponent {...this.props.children} />
        </>
      );
    }
  }
  return WithTimer;
};

export default withTimer;

MyModal.js MyModal.js

import React, { useState } from "react";
import { Modal, Button } from "react-bootstrap";

function MyModal({ show = false, modalMessage, modalHeader, onButtonClick, buttonText }) {
  const handleClick = event => {
    onButtonClick(event);
  }

  return (
    <Modal show={show} onHide={handleClick} animation={false}>
      <Modal.Header closeButton>
        <Modal.Title>{modalHeader}</Modal.Title>
      </Modal.Header>
      <Modal.Body>{modalMessage}</Modal.Body>
      <Modal.Footer>
        <Button variant="primary" onClick={handleClick}>
          {buttonText}
        </Button>
      </Modal.Footer>
    </Modal>
  );
}

export default MyModal;

Yes, you can render any components you'd like in the HOC.是的,您可以在 HOC 中渲染您想要的任何组件。 So in your case you can render a <Modal/> .因此,在您的情况下,您可以渲染<Modal/>

Of course, whether the modal is displayed or not is dynamic, so that's a perfect job for the component's state to come into play.当然,模态是否显示是动态的,因此组件的state发挥作用是一个完美的工作。 Use conditional statements in your render function to either render or not render your modal.在您的渲染 function 中使用条件语句来渲染或不渲染您的模态。

import React from 'react';
import { Modal } from 'react-bootstrap';

const withTimer = (WrappedComponent) => {
  class WithTimer extends React.Component {
    constructor(props) {
      super(props);
      this.state = {
        showWarning: false,
        showLogout: false,
        warningTime: 5000,
        signoutTime: 10000
      };

      this.events = [
        'load',
        'mousemove',
        'mousedown',
        'click',
        'scroll',
        'keypress'
      ];

      for (var i in this.events) {
        window.addEventListener(this.events[i], this.resetTimeout);
      }

      this.setTimeout();
    }

    clearTimeoutFunc = () => {
      if (this.warnTimeout) clearTimeout(this.warnTimeout);
      if (this.logoutTimeout) clearTimeout(this.logoutTimeout);
    };

    setTimeout = () => {
      this.warnTimeout = setTimeout(this.warn, this.state.warningTime);
      this.logoutTimeout = setTimeout(this.logout, this.state.signoutTime);
    };

    resetTimeout = () => {
      this.clearTimeoutFunc();
      this.setTimeout();
    };

    warn = () => {
      this.setState({ showWarning: true });
    };

    logout = () => {
      this.setState({ showLogout: true });
      // log the user out here
    };

    render() {
      let modal;
      if (this.state.showLogout) {
        modal = <Modal>...</Modal>;
      } else if (this.state.showWarning) {
        modal = <Modal>...</Modal>;
      } else {
        modal = null;
      }

      return <React.Fragment>
        <WrappedComponent {...this.props.children} />
        { modal }
      </React.Fragment>;
    }
  }
  return WithTimer;
};

export default withTimer;

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

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