Jak utworzyć Modal Reactowy (który jest dołączany do`') z przejściami?

Author: Community, 2015-03-02

8 answers

Na React conf 2015, Ryan Florencezademonstrował przy użyciu portali . Oto jak możesz utworzyć prosty Portal komponent...

var Portal = React.createClass({
  render: () => null,
  portalElement: null,
  componentDidMount() {
    var p = this.props.portalId && document.getElementById(this.props.portalId);
    if (!p) {
      var p = document.createElement('div');
      p.id = this.props.portalId;
      document.body.appendChild(p);
    }
    this.portalElement = p;
    this.componentDidUpdate();
  },
  componentWillUnmount() {
    document.body.removeChild(this.portalElement);
  },
  componentDidUpdate() {
    React.render(<div {...this.props}>{this.props.children}</div>, this.portalElement);
  }
});

I wszystko, co możesz normalnie zrobić w Reaccie, możesz zrobić wewnątrz portalu...

    <Portal className="DialogGroup">
       <ReactCSSTransitionGroup transitionName="Dialog-anim">
         { activeDialog === 1 && 
            <div key="0" className="Dialog">
              This is an animated dialog
            </div> }
       </ReactCSSTransitionGroup>
    </Portal> 

jsbin demo

Możesz też rzucić okiem na react-modal Ryana , chociaż nie używałem go, więc nie wiem, jak dobrze działa z animacją.

 59
Author: Gil Birman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-03-03 03:30:11

Napisałem moduł react-portal , który powinien ci pomóc.

 29
Author: tajo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-04-18 00:03:23

React 16 ma teraz wbudowane portale . Zamiast tego powinieneś użyć tego.

Oto wersja ES6 metody opisanej w w tym artykule :

import React from 'react';
import ReactDOM from 'react-dom';
import PropTypes from 'prop-types';

export default class BodyEnd extends React.PureComponent {

    static propTypes = {
        children: PropTypes.node,
    };

    componentDidMount() {
        this._popup = document.createElement('div');
        document.body.appendChild(this._popup);
        this._render();
    }

    componentDidUpdate() {
        this._render();
    }

    componentWillUnmount() {
        ReactDOM.unmountComponentAtNode(this._popup);
        document.body.removeChild(this._popup);
    }

    _render() {
        ReactDOM.render(this.props.children, this._popup);
    }

    render() {
        return null;
    }
}

Po prostu zawiń wszystkie elementy, które chcesz być na końcu DOM z nim:

<BodyEnd><Tooltip pos={{x,y}}>{content}</Tooltip></BodyEnd>
 12
Author: mpen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-16 02:30:45

Jak stwierdziły inne odpowiedzi, można to zrobić za pomocą portali. Począwszy od v16.0 portale są zawarte w Reaccie.

<body>
  <div id="root"></div>
  <div id="portal"></div>
</body>

Zwykle, gdy zwracasz element z metody renderowania komponentu, jest on montowany w DOM jako potomek najbliższego węzła nadrzędnego, ale dzięki portalom możesz wstawić potomek do innej lokalizacji w DOM.

const PortalComponent = ({ children, onClose }) => {
  return createPortal(
    <div className="modal" style={modalStyle} onClick={onClose}>
      {children}
    </div>,
    // get outer DOM element
    document.getElementById("portal")
  );
};

class App extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      modalOpen: false
    };
  }

  render() {
    return (
      <div style={styles}>
        <Hello name="CodeSandbox" />
        <h2>Start editing to see some magic happen {"\u2728"}</h2>
        <button onClick={() => this.setState({ modalOpen: true })}>
          Open modal
        </button>
        {this.state.modalOpen && (
          <PortalComponent onClose={() => this.setState({ modalOpen: false })}>
            <h1>This is modal content</h1>
          </PortalComponent>
        )}
      </div>
    );
  }
}

render(<App />, document.getElementById("root"));

Sprawdź działający przykład tutaj .

 7
Author: Kristaps Taube,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-09-26 18:13:28

Podstawowy problem polega na tym, że w Reaccie można montować komponent tylko do jego rodzica, co nie zawsze jest pożądanym zachowaniem. Ale jak rozwiązać ten problem?

Zrobiłem rozwiązanie, adresowane do rozwiązania tego problemu. Bardziej szczegółowa definicja problemu, src i przykłady można znaleźć tutaj: https://github.com/fckt/react-layer-stack#rationale

Uzasadnienie

react/react-dom comes zawiera 2 podstawowe założenia/pomysły:

  • każdy interfejs użytkownika jest naturalnie hierarchiczny. To dlatego mamy pomysł components, które owijają się nawzajem
  • react-dom montuje (fizycznie) komponent potomny do macierzystego węzła DOM domyślnie

Problem polega na tym, że czasami druga nieruchomość nie jest tym, czego chcesz w Twoim przypadku. Czasami chcesz zamontować swój komponent w innym fizycznym węzłem DOM i posiadać połączenie logiczne między rodzic i dziecko w tym samym czasie.

Kanonicznym przykładem jest Tooltip-like component: at some point of proces rozwoju może się okazać, że trzeba dodać kilka opis dla UI element: będzie renderował w warstwie stałej i powinien znać swoje współrzędne (czyli że UI element koord lub Mouse coords) i jednocześnie potrzebuje informacji, czy to musi być pokazana teraz lub nie, jej treść i jakiś kontekst z komponenty nadrzędne. Ten przykład pokazuje, że czasami logiczna hierarchia nie pasuje do fizycznego DOM hierarchia.

Spójrz na https://github.com/fckt/react-layer-stack/blob/master/README.md#real-world-usage-example aby zobaczyć konkretny przykład, który jest odpowiedzią na twoje pytanie:

import { Layer, LayerContext } from 'react-layer-stack'
// ... for each `object` in array of `objects`
  const modalId = 'DeleteObjectConfirmation' + objects[rowIndex].id
  return (
    <Cell {...props}>
        // the layer definition. The content will show up in the LayerStackMountPoint when `show(modalId)` be fired in LayerContext
        <Layer use={[objects[rowIndex], rowIndex]} id={modalId}> {({
            hideMe, // alias for `hide(modalId)`
            index } // useful to know to set zIndex, for example
            , e) => // access to the arguments (click event data in this example)
          <Modal onClick={ hideMe } zIndex={(index + 1) * 1000}>
            <ConfirmationDialog
              title={ 'Delete' }
              message={ "You're about to delete to " + '"' + objects[rowIndex].name + '"' }
              confirmButton={ <Button type="primary">DELETE</Button> }
              onConfirm={ this.handleDeleteObject.bind(this, objects[rowIndex].name, hideMe) } // hide after confirmation
              close={ hideMe } />
          </Modal> }
        </Layer>

        // this is the toggle for Layer with `id === modalId` can be defined everywhere in the components tree
        <LayerContext id={ modalId }> {({showMe}) => // showMe is alias for `show(modalId)`
          <div style={styles.iconOverlay} onClick={ (e) => showMe(e) }> // additional arguments can be passed (like event)
            <Icon type="trash" />
          </div> }
        </LayerContext>
    </Cell>)
// ...
 3
Author: fckt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-11-22 01:35:26

Napisałem bibliotekę, aby w tym pomóc. Unikam hacków wstawiania DOM używanych przez strategie portalowe i zamiast tego korzystam z rejestrów opartych na kontekście, aby przekazać komponenty ze źródła do celu.

Moja implementacja korzysta ze standardowych Reactowych cykli renderowania. Komponenty, które teleportujesz/wstrzykujesz/transportujesz, nie powodują podwójnego cyklu renderowania celu - wszystko dzieje się synchronicznie.

API jest również skonstruowany w sposób zniechęcający do użyj magicznych łańcuchów w kodzie, aby zdefiniować źródło / cel. Zamiast tego musisz wyraźnie utworzyć i udekorować komponenty, które będą używane jako cel (wstrzykiwanie) i źródło (wtryskiwacz). Ponieważ tego typu rzeczy są ogólnie uważane za dość magiczne, myślę, że Jawna reprezentacja komponentów (wymagająca bezpośredniego importu i użycia) może pomóc złagodzić zamieszanie na temat tego, gdzie komponent jest wstrzykiwany.

Chociaż moja biblioteka nie pozwoli Ci renderować jako bezpośrednie dziecko dokument.ciało można osiągnąć akceptowalny efekt modalny, wiążąc się z komponentem poziomu korzenia w drzewie komponentów. Planuję wkrótce dodać przykład tego przypadku użycia.

Zobacz https://github.com/ctrlplusb/react-injectables aby uzyskać więcej informacji.

 2
Author: ctrlplusb,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-04-12 10:51:06

Mam nadzieję, że to pomoże. To jest moja obecna implementacja modalu przejścia na podstawie powyższego anwsera:

  React = require 'react/addons'

  keyboard = require '../util/keyboard'
  mixinLayered = require '../mixin/layered'

  $ = React.DOM
  T = React.PropTypes
  cx = React.addons.classSet

  module.exports = React.createFactory React.createClass
    displayName: 'body-modal'
    mixins: [mixinLayered]

    propTypes:
      # this components accepts children
      name:             T.string.isRequired
      title:            T.string
      onCloseClick:     T.func.isRequired
      showCornerClose:  T.bool
      show:             T.bool.isRequired

    componentDidMount: ->
      window.addEventListener 'keydown', @onWindowKeydown

    componentWillUnmount: ->
      window.removeEventListener 'keydown', @onWindowKeydown

    onWindowKeydown: (event) ->
      if event.keyCode is keyboard.esc
        @onCloseClick()

    onCloseClick: ->
      @props.onCloseClick()

    onBackdropClick: (event) ->
      unless @props.showCornerClose
        if event.target is event.currentTarget
          @onCloseClick()

    renderLayer: ->
      className = "body-modal is-for-#{@props.name}"
      $.div className: className, onClick: @onBackdropClick,
        if @props.showCornerClose
          $.a className: 'icon icon-remove', onClick: @onCloseClick
        $.div className: 'box',
          if @props.title?
            $.div className: 'title',
              $.span className: 'name', @props.title
              $.span className: 'icon icon-remove', @onCloseClick
          @props.children

    render: ->
      $.div()
 0
Author: jiyinyiyong,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-03-06 07:19:47

Myślę, że ten kod jest mniej lub bardziej zrozumiały i obejmuje podstawowe rozwiązanie tego, czego większość ludzi szuka:

ReactDOM.render(
  <Modal />,
  document.body.appendChild( document.createElement( 'div' ) ),
)
 0
Author: Itay Grudev,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-11 16:19:07