Wie kann ich React dazu bringen, die Ansicht neu zu rendern, wenn die Größe des Browserfensters geändert wird?
Hintergrund
Ich habe einige Blöcke, die ich einzeln auf der Seite gestalten möchte, aber ich möchte auch, dass sie aktualisiert werden, wenn sich das Browserfenster ändert. Das Endergebnis wird so etwas wie das Pinterest-Layout von Ben Holland sein , das jedoch nicht nur mit jQuery, sondern auch mit React geschrieben wurde. Ich bin noch weit weg.
Code
Hier ist meine App:
var MyApp = React.createClass({
//does the http get from the server
loadBlocksFromServer: function() {
$.ajax({
url: this.props.url,
dataType: 'json',
mimeType: 'textPlain',
success: function(data) {
this.setState({data: data.events});
}.bind(this)
});
},
getInitialState: function() {
return {data: []};
},
componentWillMount: function() {
this.loadBlocksFromServer();
},
render: function() {
return (
<div>
<Blocks data={this.state.data}/>
</div>
);
}
});
React.renderComponent(
<MyApp url="url_here"/>,
document.getElementById('view')
)
Dann habe ich die Block
Komponente (entspricht a Pin
im obigen Pinterest-Beispiel):
var Block = React.createClass({
render: function() {
return (
<div class="dp-block" style={{left: this.props.top, top: this.props.left}}>
<h2>{this.props.title}</h2>
<p>{this.props.children}</p>
</div>
);
}
});
und die Liste / Sammlung von Blocks
:
var Blocks = React.createClass({
render: function() {
//I've temporarily got code that assigns a random position
//See inside the function below...
var blockNodes = this.props.data.map(function (block) {
//temporary random position
var topOffset = Math.random() * $(window).width() + 'px';
var leftOffset = Math.random() * $(window).height() + 'px';
return <Block order={block.id} title={block.summary} left={leftOffset} top={topOffset}>{block.description}</Block>;
});
return (
<div>{blockNodes}</div>
);
}
});
Frage
Sollte ich die Fenstergröße von jQuery hinzufügen? Wenn ja, wo?
$( window ).resize(function() {
// re-render the component
});
Gibt es eine "Reaktions" -Methode, um dies zu tun?
quelle
this.updateDimensions
übergeben anaddEventListener
ist nur eine bloße Funktionsreferenz, für diethis
beim Aufruf kein Wert angegeben wird . Sollte eine anonyme Funktion oder ein Aufruf von .bind () zum Hinzufügen verwendetthis
werden oder habe ich das falsch verstanden?this
für alle Methoden, die direkt in der Komponente definiert sind.innerHeight
undinnerWidth
vonwindow
. Und Sie können überspringen ,componentWillMount
wenn Sie verwendengetInitialState
zu setzenheight
undwidth
.::
Bindungssyntax derzeit nicht verfügbar ist. Sitepoint.com/bind-javascripts-this-keyword-react "Der Bindungsoperator (: :) wird nicht Teil von ES7 sein, da der ES7-Funktionssatz im Februar eingefroren wurde , der Bind-Operator ist ein Vorschlag für ES8 "@SophieAlpert ist richtig, +1, ich möchte nur eine modifizierte Version ihrer Lösung ohne jQuery bereitstellen , basierend auf dieser Antwort .
quelle
Eine sehr einfache Lösung:
quelle
componentWillUnmount()
!forceUpdate
bedingt angewendet wirdEs ist ein einfaches und kurzes Beispiel für die Verwendung von es6 ohne jQuery.
Haken
quelle
::
Bindungsoperator bei jeder Anwendung einen neuen Wert zurück. Ihr Event-Listener wird also nicht unregistriert, da SieremoveEventListener
am Ende eine andere Funktion als die ursprünglich übergebene übergebenaddEventListener
.Ab React 16.8 können Sie Hooks verwenden !
quelle
Edit 2018 : Jetzt hat React erstklassige Unterstützung für den Kontext
Ich werde versuchen, eine generische Antwort zu geben, die auf dieses spezifische Problem abzielt, aber auch auf ein allgemeineres Problem.
Wenn Sie sich nicht für Nebenwirkungen interessieren, können Sie einfach so etwas wie Packery verwenden
Wenn Sie Flux verwenden, können Sie einen Speicher erstellen, der die Fenstereigenschaften enthält, sodass Sie eine reine Renderfunktion beibehalten, ohne das Fensterobjekt jedes Mal abfragen zu müssen.
In anderen Fällen, in denen Sie eine reaktionsfähige Website erstellen möchten, aber Inline-Stile gegenüber Medienabfragen bevorzugen oder das HTML / JS-Verhalten entsprechend der Fensterbreite ändern möchten, lesen Sie weiter:
Was ist der Reaktionskontext und warum spreche ich darüber?
Reaktionskontext an befindet sich nicht in der öffentlichen API und ermöglicht die Übergabe von Eigenschaften an eine ganze Hierarchie von Komponenten.
Der Reaktionskontext ist besonders nützlich, um Dinge an Ihre gesamte App zu übergeben, die sich nie ändern (er wird von vielen Flux-Frameworks über ein Mixin verwendet). Sie können damit App-Geschäftsinvarianten speichern (wie die verbundene Benutzer-ID, sodass sie überall verfügbar ist).
Es kann aber auch verwendet werden, um Dinge zu speichern, die sich ändern können. Das Problem ist, dass, wenn sich der Kontext ändert, alle Komponenten, die ihn verwenden, neu gerendert werden sollten und dies nicht einfach ist. Die beste Lösung besteht häufig darin, die gesamte App mit dem neuen Kontext zu entfernen / erneut bereitzustellen. Denken Sie daran, dass forceUpdate nicht rekursiv ist .
Wie Sie verstehen, ist der Kontext praktisch, aber es gibt Auswirkungen auf die Leistung, wenn er sich ändert. Daher sollte er sich lieber nicht zu oft ändern.
Was in einen Kontext zu setzen
Hier sind Dinge, die sich nicht oft ändern:
Die aktuelle Benutzersprache :
Es ändert sich nicht sehr oft, und wenn dies der Fall ist, müssen wir bei der Übersetzung der gesamten App alles neu rendern: ein sehr netter Anwendungsfall für eine heiße Sprachänderung
Die Fenstereigenschaften
Breite und Höhe ändern sich nicht oft, aber wenn wir dies tun, müssen sich Layout und Verhalten möglicherweise anpassen. Für das Layout ist es manchmal einfach, es mit CSS-Medienabfragen anzupassen, aber manchmal ist es nicht so und erfordert eine andere HTML-Struktur. Für das Verhalten müssen Sie dies mit Javascript behandeln.
Sie möchten nicht bei jedem Größenänderungsereignis alles neu rendern, daher müssen Sie die Größenänderungsereignisse entprellen.
Was ich von Ihrem Problem verstehe, ist, dass Sie wissen möchten, wie viele Elemente entsprechend der Bildschirmbreite angezeigt werden sollen. Sie müssen also zuerst reaktionsfähige Haltepunkte definieren und die Anzahl der verschiedenen Layouttypen auflisten, die Sie haben können.
Zum Beispiel:
Bei Größenänderungsereignissen (entprellt) können Sie den aktuellen Layouttyp einfach abrufen, indem Sie das Fensterobjekt abfragen.
Anschließend können Sie den Layouttyp mit dem vorherigen Layouttyp vergleichen. Wenn er geändert wurde, wird die App in einem neuen Kontext erneut gerendert. Auf diese Weise wird vermieden, dass die App erneut gerendert wird, wenn der Benutzer Größenänderungsereignisse ausgelöst hat, aber tatsächlich die Der Layouttyp wurde nicht geändert, sodass Sie ihn nur bei Bedarf neu rendern.
Sobald Sie das haben, können Sie einfach den Layout-Typ in Ihrer App verwenden (über den Kontext zugänglich), um HTML, Verhalten, CSS-Klassen anzupassen. Sie kennen Ihren Layout-Typ in der React-Render-Funktion kann reaktionsschnelle Websites mithilfe von Inline-Stilen sicher schreiben und benötigt überhaupt keine Medienabfragen.
Wenn Sie Flux verwenden, können Sie einen Store anstelle des React-Kontexts verwenden. Wenn Ihre App jedoch viele reaktionsfähige Komponenten enthält, ist es möglicherweise einfacher, den Kontext zu verwenden.
quelle
Ich verwende die Lösung von @senornestor, aber um ganz richtig zu sein, müssen Sie auch den Ereignis-Listener entfernen:
Andernfalls erhalten Sie die Warnung:
quelle
Ich würde alle oben genannten Antworten überspringen und die
react-dimensions
Komponente höherer Ordnung verwenden.https://github.com/digidem/react-dimensions
Fügen Sie einfach einen einfachen
import
und einen Funktionsaufruf hinzu, und Sie können aufthis.props.containerWidth
undthis.props.containerHeight
in Ihrer Komponente zugreifen .quelle
react-dimensions
funktioniert sogar für programmgesteuert geänderte Dimensionen (z. B. hat sich die Größe eines Div geändert, was sich auf die Größe Ihres Containers auswirkt, sodass Sie Ihre Größe aktualisieren müssen). Die Antwort von Ben Alpert hilft nur bei der Größenänderung von Ereignissen im Browserfenster. Siehe hier: github.com/digidem/react-dimensions/issues/4react-dimensions
Behandelt Änderungen in der Größe des Fensters, Änderungen des Flexbox-Layouts und Änderungen aufgrund der Größenänderung von JavaScript. Ich denke, das deckt es ab. Haben Sie ein Beispiel, das nicht richtig funktioniert?window.innerWidth
,window.innerHeight
.react-dimensions
Löst den wichtigeren Teil des Problems und löst Ihren Layoutcode auch aus, wenn die Fenstergröße geändert wird (sowie wenn sich die Containergröße ändert).Dieser Code verwendet die neue React-Kontext-API :
Dann können Sie es wie folgt in Ihrem Code verwenden:
quelle
Sie müssen nicht unbedingt ein erneutes Rendern erzwingen.
Dies hilft OP möglicherweise nicht, aber in meinem Fall musste ich nur die Attribute
width
undheight
auf meiner Zeichenfläche aktualisieren (was mit CSS nicht möglich ist).Es sieht aus wie das:
quelle
Ich bin mir nicht sicher, ob dies der beste Ansatz ist, aber was für mich funktioniert hat, war zuerst einen Store zu erstellen. Ich habe ihn WindowStore genannt:
Dann habe ich diesen Speicher in meinen Komponenten verwendet, irgendwie wie folgt:
Zu Ihrer Information, diese Dateien wurden teilweise gekürzt.
quelle
onresize
könnte einWindowResizedAction
.Ich weiß, dass dies beantwortet wurde, dachte aber nur, ich würde meine Lösung als Top-Antwort teilen, obwohl sie großartig ist, könnte sie jetzt etwas veraltet sein.
Der einzige Unterschied besteht darin, dass ich die updateWindowDimensions für das Größenänderungsereignis entprelle (nur alle 200 ms), um die Leistung ein wenig zu steigern, ABER sie nicht entprellen, wenn sie auf ComponentDidMount aufgerufen werden.
Ich fand, dass die Entprellung es manchmal ziemlich verzögert machte, zu montieren, wenn Sie eine Situation haben, in der es oft montiert wird.
Nur eine kleine Optimierung, aber ich hoffe, es hilft jemandem!
quelle
initUpdateWindowDimensions
Methode?Nur um die zu verwendende @ senornestor-Lösung
forceUpdate
und die @ gkri-Lösung zum Entfernen desresize
Ereignis-Listeners beim Aufheben der Bereitstellung von Komponenten zu verbessern :bind(this)
im KonstruktorEine andere Methode besteht darin, einfach einen "Dummy" -Zustand zu verwenden, anstatt
forceUpdate
:quelle
Es muss nur die Größenänderungsereignisfunktion definiert werden.
Aktualisieren Sie dann die Größe des Renderers (Leinwand) und weisen Sie der Kamera ein neues Seitenverhältnis zu.
Das Aus- und Aussteigen ist meiner Meinung nach eine verrückte Lösung ....
Unten ist die Halterung, falls erforderlich.
quelle
Wollte diese ziemlich coole Sache teilen, die ich gerade mit gefunden habe
window.matchMedia
.matches
Gibt true oder false zurück, wenn das Fenster höher oder niedriger als der angegebene Wert für die maximale Breite ist. Dies bedeutet, dass der Listener nicht gedrosselt werden muss, da matchMedia nur einmal ausgelöst wird, wenn sich der Boolesche Wert ändert.Mein Code kann einfach so angepasst werden, dass
useState
er die booleschen matchMedia-Rückgaben enthält und zum bedingten Rendern einer Komponente, einer Feueraktion usw. verwendet wird.quelle
Musste es im Konstruktor an 'this' binden, damit es mit der Klassensyntax funktioniert
quelle
Vielen Dank für die Antworten. Hier ist mein React + Recompose . Es ist eine Funktion hoher Ordnung, die die Eigenschaften
windowHeight
undwindowWidth
für die Komponente enthält.quelle
https://github.com/renatorib/react-sizes ist ein HOC, um dies zu tun und gleichzeitig eine gute Leistung aufrechtzuerhalten.
quelle
Aus diesem Grund ist es besser, wenn Sie diese Daten aus CSS- oder JSON-Dateidaten verwenden und dann mit diesen Daten einen neuen Status mit this.state festlegen ({width: "some value", height: "some value"}); oder Schreiben von Code, der Daten mit Bildschirmbreite in der Selbstarbeit verwendet, wenn Sie reaktionsschnelle Showbilder wünschen
quelle