Die Renderer-Ansicht im Browser ändert die Größe mit React

313

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 BlockKomponente (entspricht a Pinim 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?

Digibake
quelle

Antworten:

531

React Hooks verwenden:

Sie können einen benutzerdefinierten Hook definieren, der das Fensterereignis abhört resize:

import React, { useLayoutEffect, useState } from 'react';

function useWindowSize() {
  const [size, setSize] = useState([0, 0]);
  useLayoutEffect(() => {
    function updateSize() {
      setSize([window.innerWidth, window.innerHeight]);
    }
    window.addEventListener('resize', updateSize);
    updateSize();
    return () => window.removeEventListener('resize', updateSize);
  }, []);
  return size;
}

function ShowWindowDimensions(props) {
  const [width, height] = useWindowSize();
  return <span>Window size: {width} x {height}</span>;
}

Der Vorteil hierbei ist, dass die Logik gekapselt ist und Sie diesen Hook überall dort verwenden können, wo Sie die Fenstergröße verwenden möchten.

React-Klassen verwenden:

Sie können in componentDidMount so etwas wie diese Komponente abhören, die nur die Fensterabmessungen anzeigt (wie <span>Window size: 1024 x 768</span>):

import React from 'react';

class ShowWindowDimensions extends React.Component {
  state = { width: 0, height: 0 };
  render() {
    return <span>Window size: {this.state.width} x {this.state.height}</span>;
  }
  updateDimensions = () => {
    this.setState({ width: window.innerWidth, height: window.innerHeight });
  };
  componentDidMount() {
    window.addEventListener('resize', this.updateDimensions);
  }
  componentWillUnmount() {
    window.removeEventListener('resize', this.updateDimensions);
  }
}
Sophie Alpert
quelle
5
Wie funktioniert das? Das this.updateDimensionsübergeben an addEventListenerist nur eine bloße Funktionsreferenz, für die thisbeim Aufruf kein Wert angegeben wird . Sollte eine anonyme Funktion oder ein Aufruf von .bind () zum Hinzufügen verwendet thiswerden oder habe ich das falsch verstanden?
Fadedbee
24
@chrisdew Ich bin etwas spät dran, aber React bindet automatisch thisfür alle Methoden, die direkt in der Komponente definiert sind.
Michelle Tilley
3
@MattDell Ja, ES6-Klassen sind nur normale Klassen, daher keine automatische Bindung mit ihnen.
Michelle Tilley
30
Keine jQuery erforderlich - verwenden innerHeightund innerWidthvon window. Und Sie können überspringen , componentWillMountwenn Sie verwenden getInitialStatezu setzen heightund width.
Seufzer
2
@MattDell sieht so aus, als ob die ::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 "
Jarrod Smith
130

@SophieAlpert ist richtig, +1, ich möchte nur eine modifizierte Version ihrer Lösung ohne jQuery bereitstellen , basierend auf dieser Antwort .

var WindowDimensions = React.createClass({
    render: function() {
        return <span>{this.state.width} x {this.state.height}</span>;
    },
    updateDimensions: function() {

    var w = window,
        d = document,
        documentElement = d.documentElement,
        body = d.getElementsByTagName('body')[0],
        width = w.innerWidth || documentElement.clientWidth || body.clientWidth,
        height = w.innerHeight|| documentElement.clientHeight|| body.clientHeight;

        this.setState({width: width, height: height});
        // if you are using ES2015 I'm pretty sure you can do this: this.setState({width, height});
    },
    componentWillMount: function() {
        this.updateDimensions();
    },
    componentDidMount: function() {
        window.addEventListener("resize", this.updateDimensions);
    },
    componentWillUnmount: function() {
        window.removeEventListener("resize", this.updateDimensions);
    }
});
André Pena
quelle
funktioniert nur, wenn Sie Ihre IE-bezogenen Polyfills für Vanilla JS-Ereignis-Listener eingerichtet haben
nnnn
@nnnn kannst du das näher erläutern? Ich gebe zu, ich habe dies nur in Chrome getestet. Wollen Sie damit sagen, dass window.addEventListener im IE ohne Polyfills nicht funktioniert?
André Pena
2
@andrerpena caniuse.com/#search=addeventlistener zeigt an, dass ie8 ein Problem haben würde
nnnn
2
@nnnn. Aha. Ja. Meine Lösung funktioniert also nicht mit IE 8, sondern ab 9 :). Vielen Dank.
André Pena
35
Interessiert sich noch jemand wirklich für IE8? Oder ist es nur Gewohnheit?
frostymarvelous
48

Eine sehr einfache Lösung:

resize = () => this.forceUpdate()

componentDidMount() {
  window.addEventListener('resize', this.resize)
}

componentWillUnmount() {
  window.removeEventListener('resize', this.resize)
}
Senornestor
quelle
12
Vergessen Sie nicht, das Force-Update zu drosseln, da es sonst sehr unangenehm aussehen wird.
k2snowman69
4
Vergessen Sie auch nicht, den Listener zu entfernen componentWillUnmount()!
Jemar Jones
Dies ist die beste Lösung, wenn es gedrosselt wird. Ich meine, wenn das forceUpdatebedingt angewendet wird
asmmahmud
1
Es ist nicht das forceUpdate (das Ding, das aufgerufen wird), das gedrosselt werden muss, sondern das Auslösen eines Größenänderungsereignisses, das gedrosselt werden muss (das Ding, das ausgelöst wird). Größenänderungsereignisse können technisch an jedem Pixel aufgerufen werden, wenn Sie die Größe eines Fensters von groß nach klein ändern. Wenn ein Benutzer dies schnell erledigt, sind das mehr Ereignisse, als Sie interessieren. Schlimmer noch, Sie binden den UI-Thread an den Javascript-Thread, was bedeutet, dass Ihre App ein ernsthaft langsames Gefühl bekommt, wenn sie versucht, jedes Ereignis einzeln zu behandeln.
k2snowman69
1
Anstatt die Größenänderungsfunktion für jedes Pixel auszuführen, führen Sie sie in kurzer Zeit aus, um die Illusion von Fluidität zu erzeugen, die Sie immer beim ersten und letzten Ereignis behandeln, und geben so das Gefühl, dass die Größenänderung flüssig gehandhabt wird.
k2snowman69
42

Es ist ein einfaches und kurzes Beispiel für die Verwendung von es6 ohne jQuery.

import React, { Component } from 'react';

export default class CreateContact extends Component {
  state = {
    windowHeight: undefined,
    windowWidth: undefined
  }

  handleResize = () => this.setState({
    windowHeight: window.innerHeight,
    windowWidth: window.innerWidth
  });

  componentDidMount() {
    this.handleResize();
    window.addEventListener('resize', this.handleResize)
  }

  componentWillUnmount() {
    window.removeEventListener('resize', this.handleResize)
  }

  render() {
    return (
      <span>
        {this.state.windowWidth} x {this.state.windowHeight}
      </span>
    );
  }
}

Haken

import React, { useEffect, useState } from "react";

let App = () => {
  const [windowWidth, setWindowWidth] = useState(0);
  const [windowHeight, setWindowHeight] = useState(0);
  let resizeWindow = () => {
    setWindowWidth(window.innerWidth);
    setWindowHeight(window.innerHeight);
  };

  useEffect(() => {
    resizeWindow();
    window.addEventListener("resize", resizeWindow);
    return () => window.removeEventListener("resize", resizeWindow);
  }, []);

  return (
    <div>
      <span>
        {windowWidth} x {windowHeight}
      </span>
    </div>
  );
};
Stimme
quelle
4
Dies ist eine nette, prägnante Antwort, aber AFAICT hat einen Fehler: Wenn ich mich nicht irre, gibt der ::Bindungsoperator bei jeder Anwendung einen neuen Wert zurück. Ihr Event-Listener wird also nicht unregistriert, da Sie removeEventListeneram Ende eine andere Funktion als die ursprünglich übergebene übergeben addEventListener.
Natevw
21

Ab React 16.8 können Sie Hooks verwenden !

/* globals window */
import React, { useState, useEffect } from 'react'
import _debounce from 'lodash.debounce'

const Example = () => {
  const [width, setWidth] = useState(window.innerWidth)

  useEffect(() => {
    const handleResize = _debounce(() => setWidth(window.innerWidth), 100)

    window.addEventListener('resize', handleResize);

    return () => {
      window.removeEventListener('resize', handleResize);
    }
  }, [])

  return <>Width: {width}</>
}
noetix
quelle
13

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

  • Invarianten: wie die verbundene Benutzer-ID, sessionToken, was auch immer ...
  • Dinge, die sich nicht oft ändern

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:

  • Layout "1col" für Breite <= 600
  • Layout "2col" für 600 <Breite <1000
  • Layout "3col" für 1000 <= Breite

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.

Sebastien Lorber
quelle
10

Ich verwende die Lösung von @senornestor, aber um ganz richtig zu sein, müssen Sie auch den Ereignis-Listener entfernen:

componentDidMount() {
    window.addEventListener('resize', this.handleResize);
}

componentWillUnmount(){
    window.removeEventListener('resize', this.handleResize);
}

handleResize = () => {
    this.forceUpdate();
};

Andernfalls erhalten Sie die Warnung:

Warnung: forceUpdate (...): Kann nur eine gemountete oder gemountete Komponente aktualisieren. Dies bedeutet normalerweise, dass Sie forceUpdate () für eine nicht gemountete Komponente aufgerufen haben. Dies ist ein No-Op. Bitte überprüfen Sie den Code für die XXX-Komponente.

gkri
quelle
1
Sie erhalten die Warnung aus einem Grund: Was Sie tun, ist schlechte Praxis. Ihre render () -Funktion sollte eine reine Funktion von Requisiten und Status sein. In Ihrem Fall sollten Sie die neue Größe im Status speichern.
Zoran404
7

Ich würde alle oben genannten Antworten überspringen und die react-dimensionsKomponente höherer Ordnung verwenden.

https://github.com/digidem/react-dimensions

Fügen Sie einfach einen einfachen importund einen Funktionsaufruf hinzu, und Sie können auf this.props.containerWidthund this.props.containerHeightin Ihrer Komponente zugreifen .

// Example using ES6 syntax
import React from 'react'
import Dimensions from 'react-dimensions'

class MyComponent extends React.Component {
  render() (
    <div
      containerWidth={this.props.containerWidth}
      containerHeight={this.props.containerHeight}
    >
    </div>
  )
}

export default Dimensions()(MyComponent) // Enhanced component
MindJuice
quelle
1
Dies gibt nicht die Größe des Fensters an, sondern nur den Container.
Mjtamlyn
3
Ja, das ist der springende Punkt. Die Größe des Fensters ist trivial zu finden. Die Größe eines Containers ist viel schwieriger zu finden und für eine React-Komponente viel nützlicher. Die neueste Version von react-dimensionsfunktioniert 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/4
MindJuice
1
react-dimensionsBehandelt Ä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?
MindJuice
2
Fenstergröße ist trivial zu bekommen. Dafür ist keine Bibliothek erforderlich: window.innerWidth, window.innerHeight. react-dimensionsLö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).
MindJuice
1
Dieses Projekt wird nicht mehr aktiv gepflegt.
Parabolord
7

Dieser Code verwendet die neue React-Kontext-API :

  import React, { PureComponent, createContext } from 'react';

  const { Provider, Consumer } = createContext({ width: 0, height: 0 });

  class WindowProvider extends PureComponent {
    state = this.getDimensions();

    componentDidMount() {
      window.addEventListener('resize', this.updateDimensions);
    }

    componentWillUnmount() {
      window.removeEventListener('resize', this.updateDimensions);
    }

    getDimensions() {
      const w = window;
      const d = document;
      const documentElement = d.documentElement;
      const body = d.getElementsByTagName('body')[0];
      const width = w.innerWidth || documentElement.clientWidth || body.clientWidth;
      const height = w.innerHeight || documentElement.clientHeight || body.clientHeight;

      return { width, height };
    }

    updateDimensions = () => {
      this.setState(this.getDimensions());
    };

    render() {
      return <Provider value={this.state}>{this.props.children}</Provider>;
    }
  }

Dann können Sie es wie folgt in Ihrem Code verwenden:

<WindowConsumer>
  {({ width, height }) =>  //do what you want}
</WindowConsumer>
Albert Olivé
quelle
6

Sie müssen nicht unbedingt ein erneutes Rendern erzwingen.

Dies hilft OP möglicherweise nicht, aber in meinem Fall musste ich nur die Attribute widthund heightauf meiner Zeichenfläche aktualisieren (was mit CSS nicht möglich ist).

Es sieht aus wie das:

import React from 'react';
import styled from 'styled-components';
import {throttle} from 'lodash';

class Canvas extends React.Component {

    componentDidMount() {
        window.addEventListener('resize', this.resize);
        this.resize();
    }

    componentWillUnmount() {
        window.removeEventListener('resize', this.resize);
    }

    resize = throttle(() => {
        this.canvas.width = this.canvas.parentNode.clientWidth;
        this.canvas.height = this.canvas.parentNode.clientHeight;
    },50)

    setRef = node => {
        this.canvas = node;
    }

    render() {
        return <canvas className={this.props.className} ref={this.setRef} />;
    }
}

export default styled(Canvas)`
   cursor: crosshair;
`
mpen
quelle
5

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:

import {assign, events} from '../../libs';
import Dispatcher from '../dispatcher';
import Constants from '../constants';

let CHANGE_EVENT = 'change';
let defaults = () => {
    return {
        name: 'window',
        width: undefined,
        height: undefined,
        bps: {
            1: 400,
            2: 600,
            3: 800,
            4: 1000,
            5: 1200,
            6: 1400
        }
    };
};
let save = function(object, key, value) {
    // Save within storage
    if(object) {
        object[key] = value;
    }

    // Persist to local storage
    sessionStorage[storage.name] = JSON.stringify(storage);
};
let storage;

let Store = assign({}, events.EventEmitter.prototype, {
    addChangeListener: function(callback) {
        this.on(CHANGE_EVENT, callback);
        window.addEventListener('resize', () => {
            this.updateDimensions();
            this.emitChange();
        });
    },
    emitChange: function() {
        this.emit(CHANGE_EVENT);
    },
    get: function(keys) {
        let value = storage;

        for(let key in keys) {
            value = value[keys[key]];
        }

        return value;
    },
    initialize: function() {
        // Set defaults
        storage = defaults();
        save();
        this.updateDimensions();
    },
    removeChangeListener: function(callback) {
        this.removeListener(CHANGE_EVENT, callback);
        window.removeEventListener('resize', () => {
            this.updateDimensions();
            this.emitChange();
        });
    },
    updateDimensions: function() {
        storage.width =
            window.innerWidth ||
            document.documentElement.clientWidth ||
            document.body.clientWidth;
        storage.height =
            window.innerHeight ||
            document.documentElement.clientHeight ||
            document.body.clientHeight;
        save();
    }
});

export default Store;

Dann habe ich diesen Speicher in meinen Komponenten verwendet, irgendwie wie folgt:

import WindowStore from '../stores/window';

let getState = () => {
    return {
        windowWidth: WindowStore.get(['width']),
        windowBps: WindowStore.get(['bps'])
    };
};

export default React.createClass(assign({}, base, {
    getInitialState: function() {
        WindowStore.initialize();

        return getState();
    },
    componentDidMount: function() {
        WindowStore.addChangeListener(this._onChange);
    },
    componentWillUnmount: function() {
        WindowStore.removeChangeListener(this._onChange);
    },
    render: function() {
        if(this.state.windowWidth < this.state.windowBps[2] - 1) {
            // do something
        }

        // return
        return something;
    },
    _onChange: function() {
        this.setState(getState());
    }
}));

Zu Ihrer Information, diese Dateien wurden teilweise gekürzt.

David Sinclair
quelle
1
Der Speicherstatus sollte sich nur als Reaktion auf eine ausgelöste Aktion ändern. Dies ist definitiv kein guter Weg, dies zu tun.
frostymarvelous
2
@frostymarvelous in der Tat, vielleicht besser in die Komponente verschoben als in den anderen Antworten.
David Sinclair
@ DavidSinclair Oder noch besser, onresizekönnte ein WindowResizedAction.
John Weisz
1
Das ist übertrieben.
Jason Rice
5

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.

    constructor (props) {
      super(props)

      this.state = { width: '0', height: '0' }

      this.initUpdateWindowDimensions = this.updateWindowDimensions.bind(this)
      this.updateWindowDimensions = debounce(this.updateWindowDimensions.bind(this), 200)
    }

    componentDidMount () {
      this.initUpdateWindowDimensions()
      window.addEventListener('resize', this.updateWindowDimensions)
    }

    componentWillUnmount () {
      window.removeEventListener('resize', this.updateWindowDimensions)
    }

    updateWindowDimensions () {
      this.setState({ width: window.innerWidth, height: window.innerHeight })
    }

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!

Matt Wills
quelle
Wo ist die Definition der initUpdateWindowDimensionsMethode?
Matt
Es ist im Konstruktor definiert :) Es ist nur updateWindowDimensions, das an die Komponente gebunden ist, aber ohne das Entprellen.
Matt Wills
3

Nur um die zu verwendende @ senornestor-Lösung forceUpdateund die @ gkri-Lösung zum Entfernen des resizeEreignis-Listeners beim Aufheben der Bereitstellung von Komponenten zu verbessern :

  1. Vergessen Sie nicht, den Anruf zu drosseln (oder zu entprellen), um die Größe zu ändern
  2. Stellen Sie sicher, dass bind(this)im Konstruktor
import React from 'react'
import { throttle } from 'lodash'

class Foo extends React.Component {
  constructor(props) {
    super(props)
    this.resize = throttle(this.resize.bind(this), 100)
  }

  resize = () => this.forceUpdate()

  componentDidMount() {
    window.addEventListener('resize', this.resize)
  }

  componentWillUnmount() {
    window.removeEventListener('resize', this.resize)
  }

  render() {
    return (
      <div>{window.innerWidth} x {window.innerHeight}</div>
    )
  }
}

Eine andere Methode besteht darin, einfach einen "Dummy" -Zustand zu verwenden, anstatt forceUpdate:

import React from 'react'
import { throttle } from 'lodash'

class Foo extends React.Component {
  constructor(props) {
    super(props)
    this.state = { foo: 1 }
    this.resize = throttle(this.resize.bind(this), 100)
  }

  resize = () => this.setState({ foo: 1 })

  componentDidMount() {
    window.addEventListener('resize', this.resize)
  }

  componentWillUnmount() {
    window.removeEventListener('resize', this.resize)
  }

  render() {
    return (
      <div>{window.innerWidth} x {window.innerHeight}</div>
    )
  }
}
Kimbaudi
quelle
2
componentDidMount() {

    // Handle resize
    window.addEventListener('resize', this.handleResize);
}




handleResize = () => {
    this.renderer.setSize(this.mount.clientWidth, this.mount.clientHeight);
    this.camera.aspect = this.mount.clientWidth / this.mount.clientHeight;
    this.camera.updateProjectionMatrix();
};

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.

            <div
                className={this.state.canvasActive ? 'canvasContainer isActive' : 'canvasContainer'}
                ref={mount => {
                    this.mount = mount;
                }}
            />
Nicolay Hekkens
quelle
2

Wollte diese ziemlich coole Sache teilen, die ich gerade mit gefunden habe window.matchMedia

const mq = window.matchMedia('(max-width: 768px)');

  useEffect(() => {
    // initial check to toggle something on or off
    toggle();

    // returns true when window is <= 768px
    mq.addListener(toggle);

    // unmount cleanup handler
    return () => mq.removeListener(toggle);
  }, []);

  // toggle something based on matchMedia event
  const toggle = () => {
    if (mq.matches) {
      // do something here
    } else {
      // do something here
    }
  };

.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 useStateer die booleschen matchMedia-Rückgaben enthält und zum bedingten Rendern einer Komponente, einer Feueraktion usw. verwendet wird.

Riley Brown
quelle
1

Musste es im Konstruktor an 'this' binden, damit es mit der Klassensyntax funktioniert

class MyComponent extends React.Component {
  constructor(props) {
    super(props)
    this.resize = this.resize.bind(this)      
  }
  componentDidMount() {
    window.addEventListener('resize', this.resize)
  }
  componentWillUnmount() {
    window.removeEventListener('resize', this.resize)
  }
}
Jim Perris
quelle
1

Vielen Dank für die Antworten. Hier ist mein React + Recompose . Es ist eine Funktion hoher Ordnung, die die Eigenschaften windowHeightund windowWidthfür die Komponente enthält.

const withDimensions = compose(
 withStateHandlers(
 ({
   windowHeight,
   windowWidth
 }) => ({
   windowHeight: window.innerHeight,
   windowWidth: window.innerWidth
 }), {
  handleResize: () => () => ({
    windowHeight: window.innerHeight,
    windowWidth: window.innerWidth
  })
 }),
 lifecycle({
   componentDidMount() {
   window.addEventListener('resize', this.props.handleResize);
 },
 componentWillUnmount() {
  window.removeEventListener('resize');
 }})
)
dumorango
quelle
1

https://github.com/renatorib/react-sizes ist ein HOC, um dies zu tun und gleichzeitig eine gute Leistung aufrechtzuerhalten.

import React from 'react'
import withSizes from 'react-sizes'

@withSizes(({ width }) => ({ isMobile: width < 480 }))
class MyComponent extends Component {
  render() {
    return <div>{this.props.isMobile ? 'Is Mobile' : 'Is Not Mobile'}</div>
  }
}

export default MyComponent
Russell Cohen
quelle
0

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

Miodrag Trajanovic
quelle