Jak utworzyć Modal Reactowy (który jest dołączany do`') z przejściami?
W tej odpowiedzi jest modal https://stackoverflow.com/a/26789089/883571 {[3] } czyli tworzenie modalu opartego na Reaccie poprzez dodanie go do <body>
. Jednak okazało się, że nie jest kompatybilny z dodatkami przejściowymi dostarczonymi przez Reacta.
Jak utworzyć taki z przejściami (podczas enter I leave)?
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ą.
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.
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>
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 .
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ę nawzajemreact-dom
montuje (fizycznie) komponent potomny do macierzystego węzła DOM domyślnieProblem 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 żeUI 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>)
// ...
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.
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()
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' ) ),
)
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