Rendre la vue sur le navigateur redimensionner avec React

313

Comment puis-je demander à React de restituer la vue lorsque la fenêtre du navigateur est redimensionnée?

Contexte

J'ai des blocs que je souhaite mettre en page individuellement sur la page, mais je veux également qu'ils soient mis à jour lorsque la fenêtre du navigateur change. Le résultat final sera quelque chose comme la mise en page Pinterest de Ben Holland , mais écrit en utilisant React et pas seulement jQuery. Je suis encore loin.

Code

Voici mon application:

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')
)

Ensuite, j'ai le Blockcomposant (équivalent à un Pindans l'exemple Pinterest ci-dessus):

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>
    );
  }
});

et la liste / collection de 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>
    );
  }
});

Question

Dois-je ajouter un redimensionnement de la fenêtre de jQuery? Si oui, où?

$( window ).resize(function() {
  // re-render the component
});

Existe-t-il une façon plus «réactive» de procéder?

digibake
la source

Réponses:

531

Utilisation des crochets React:

Vous pouvez définir un hook personnalisé qui écoute l' resizeévénement window , quelque chose comme ceci:

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>;
}

L'avantage ici est que la logique est encapsulée, et vous pouvez utiliser ce crochet où vous voulez utiliser la taille de la fenêtre.

Utilisation des classes React:

Vous pouvez écouter dans componentDidMount, quelque chose comme ce composant qui affiche simplement les dimensions de la fenêtre (comme <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
la source
5
Comment cela marche-t-il? Le this.updateDimensionspassé à addEventListenerest juste une référence de fonction nue qui n'aura aucune valeur thislors de son appel. Faut-il ajouter une fonction anonyme ou un appel .bind () this, ou ai-je mal compris?
fadedbee
24
@chrisdew Je suis un peu en retard ici, mais React se lie automatiquement thispour toutes les méthodes définies directement sur le composant.
Michelle Tilley
3
@MattDell oui, les classes ES6 ne sont que des classes normales, donc pas de liaison automatique avec elles.
Michelle Tilley
30
Aucun jQuery nécessaire - utiliser innerHeightet à innerWidthpartir de window. Et vous pouvez ignorer componentWillMountsi vous utilisez getInitialStatepour définir heightet width.
sighrobot
2
@MattDell semble que la ::syntaxe de liaison soit sortie pour l'instant sitepoint.com/bind-javascripts-this-keyword-react "l'opérateur de liaison (: :) ne fera pas partie d'ES7, car l'ensemble des fonctionnalités ES7 a été gelé en février , l'opérateur de liaison est une proposition pour ES8 "
Jarrod Smith
130

@SophieAlpert a raison, +1, je veux juste fournir une version modifiée de sa solution, sans jQuery , basée sur cette réponse .

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
la source
ne fonctionne que lorsque vos polyfills liés à IE sont configurés pour les auditeurs d'événements JS vanilla
nnnn
@nnnn pouvez-vous élaborer? J'avoue que je n'ai testé cela que dans Chrome. Voulez-vous dire que window.addEventListener ne fonctionnera pas sur IE sans polyfills?
André Pena
2
@andrerpena caniuse.com/#search=addeventlistener indique que ie8 aurait un problème
nnnn
2
@nnnn. Je vois. Oui .. Donc ma solution ne fonctionne pas sur IE 8, mais fonctionne à partir de 9 :). Merci.
André Pena
35
Quelqu'un se soucie-t-il vraiment encore d'IE8? Ou est-ce juste une habitude?
frostymarvelous
48

Une solution très simple:

resize = () => this.forceUpdate()

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

componentWillUnmount() {
  window.removeEventListener('resize', this.resize)
}
senornestor
la source
12
N'oubliez pas d'accélérer la mise à jour de la force ou cela va paraître très glitch.
k2snowman69
4
N'oubliez pas non plus de supprimer l'auditeur componentWillUnmount()!
Jemar Jones
C'est la meilleure solution si elle est limitée. Je veux dire si l' forceUpdateapplication est conditionnelle
asmmahmud
1
Ce n'est pas la forceUpdate (la chose appelée) qui doit être étranglée, c'est le tir d'événement de redimensionnement qui doit être étranglé (la chose qui se déclenche). Les événements de redimensionnement peuvent techniquement être appelés à chaque pixel lorsque vous redimensionnez une fenêtre de grande à petite. Lorsqu'un utilisateur le fait rapidement, cela représente plus d'événements que vous ne vous en souciez. Pire encore, vous liez le thread d'interface utilisateur au thread Javascript, ce qui signifie que votre application commencera à ressentir une lenteur sérieuse lorsqu'elle essaiera de gérer chaque événement individuellement.
k2snowman69
1
Au lieu d'exécuter la fonction de redimensionnement à chaque pixel, vous l'exécutez à un court laps de temps périodique pour donner l'illusion de fluidité que vous gérez toujours le premier et le dernier événement donnant ainsi l'impression qu'il gère avec fluidité le redimensionnement.
k2snowman69
42

C'est un exemple simple et court d'utilisation d'es6 sans 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>
    );
  }
}

crochets

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>
  );
};
voix
la source
4
C'est une réponse agréable et succincte, mais AFAICT a un bug: à moins que je ne me trompe, l' ::opérateur de liaison renvoie une nouvelle valeur chaque fois qu'elle est appliquée. Ainsi, votre écouteur d'événements ne sera pas réellement enregistré, car vous removeEventListenerfinirez par transmettre une fonction différente de celle à laquelle il a été transmis à l'origine addEventListener.
natevw
21

À partir de React 16.8, vous pouvez utiliser des crochets !

/* 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
la source
13

Edit 2018 : maintenant React a un support de première classe pour le contexte


Je vais essayer de donner une réponse générique, qui cible ce problème spécifique mais aussi un problème plus général.

Si vous ne vous souciez pas des bibliothèques d' effets secondaires, vous pouvez simplement utiliser quelque chose comme Packery

Si vous utilisez Flux, vous pouvez créer un magasin contenant les propriétés de la fenêtre afin de conserver une fonction de rendu pure sans avoir à interroger l'objet fenêtre à chaque fois.

Dans d'autres cas, lorsque vous souhaitez créer un site Web réactif mais que vous préférez les styles en ligne React aux requêtes multimédias ou que le comportement HTML / JS change en fonction de la largeur de la fenêtre, continuez à lire:

Qu'est-ce que le contexte React et pourquoi j'en parle

Le contexte React n'est pas dans l'API publique et permet de transmettre des propriétés à toute une hiérarchie de composants.

Le contexte React est particulièrement utile pour transmettre à l'ensemble de votre application des choses qui ne changent jamais (il est utilisé par de nombreux frameworks Flux via un mixin). Vous pouvez l'utiliser pour stocker des invariants commerciaux d'application (comme l'ID utilisateur connecté, afin qu'il soit disponible partout).

Mais il peut également être utilisé pour stocker des choses qui peuvent changer. Le problème est que lorsque le contexte change, tous les composants qui l'utilisent doivent être restitués et ce n'est pas facile à faire, la meilleure solution est souvent de démonter / remonter l'application entière avec le nouveau contexte. N'oubliez pas que forceUpdate n'est pas récursif .

Donc, comme vous le comprenez, le contexte est pratique, mais il y a un impact sur les performances lorsqu'il change, il ne devrait donc pas changer trop souvent.

Que mettre en contexte

  • Invariants: comme le userId connecté, sessionToken, peu importe ...
  • Des choses qui ne changent pas souvent

Voici des choses qui ne changent pas souvent:

La langue actuelle de l'utilisateur :

Il ne change pas très souvent, et quand il le fait, comme toute l'application est traduite, nous devons tout restituer: une très belle utilisation du changement de langage à chaud

Les propriétés de la fenêtre

La largeur et la hauteur ne changent pas souvent, mais lorsque nous le faisons, notre disposition et notre comportement peuvent devoir s'adapter. Pour la mise en page, il est parfois facile de personnaliser avec les médias CSS, mais parfois ce n'est pas le cas et nécessite une structure HTML différente. Pour le comportement, vous devez gérer cela avec Javascript.

Vous ne voulez pas tout restituer à chaque événement de redimensionnement, vous devez donc annuler les événements de redimensionnement.

Ce que je comprends de votre problème, c'est que vous voulez savoir combien d'éléments afficher en fonction de la largeur de l'écran. Vous devez donc d'abord définir des points d'arrêt réactifs et énumérer le nombre de types de disposition différents que vous pouvez avoir.

Par exemple:

  • Disposition "1col", pour une largeur <= 600
  • Disposition "2col", pour 600 <largeur <1000
  • Disposition "3col", pour 1000 <= largeur

Lors des événements de redimensionnement (anti-rebond), vous pouvez facilement obtenir le type de mise en page actuel en interrogeant l'objet fenêtre.

Ensuite, vous pouvez comparer le type de mise en page avec l'ancien type de mise en page, et s'il a changé, restituez l'application avec un nouveau contexte: cela permet d'éviter de restituer l'application du tout lorsque l'utilisateur a déclenché des événements de redimensionnement mais en fait le le type de mise en page n'a pas changé, vous ne devez donc effectuer un nouveau rendu qu'en cas de besoin.

Une fois que vous avez cela, vous pouvez simplement utiliser le type de mise en page à l'intérieur de votre application (accessible via le contexte) afin de pouvoir personnaliser le HTML, le comportement, les classes CSS ... Vous connaissez votre type de mise en page dans la fonction de rendu React, cela signifie donc que vous peut écrire en toute sécurité des sites Web réactifs en utilisant des styles en ligne, et n'a pas besoin du tout de mediaqueries.

Si vous utilisez Flux, vous pouvez utiliser un magasin au lieu du contexte React, mais si votre application a beaucoup de composants réactifs, il est peut-être plus simple d'utiliser le contexte?

Sébastien Lorber
la source
10

J'utilise la solution de @senornestor, mais pour être tout à fait correct, vous devez également supprimer l'écouteur d'événements:

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

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

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

Sinon, vous obtiendrez l'avertissement:

Avertissement: forceUpdate (...): ne peut mettre à jour qu'un composant monté ou monté. Cela signifie généralement que vous avez appelé forceUpdate () sur un composant non monté. C'est un no-op. Veuillez vérifier le code du composant XXX.

gkri
la source
1
Vous recevez l'avertissement pour une raison: ce que vous faites est une mauvaise pratique. Votre fonction render () devrait être une pure fonction d'accessoires et d'état. Dans votre cas, vous devez enregistrer la nouvelle taille en l'état.
zoran404
7

Je sauterais toutes les réponses ci-dessus et commencerais à utiliser le react-dimensionscomposant d'ordre supérieur.

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

Ajoutez simplement un simple importet un appel de fonction, et vous pouvez accéder à this.props.containerWidthet this.props.containerHeightdans votre composant.

// 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
la source
1
Cela ne donne pas la taille de la fenêtre, juste le conteneur.
mjtamlyn
3
Oui, c'est tout le problème. La taille de la fenêtre est triviale à trouver. La taille d'un conteneur est beaucoup plus difficile à trouver et beaucoup plus utile pour un composant React. La dernière version de react-dimensionsfonctionne même pour les dimensions modifiées par programme (par exemple, la taille d'un div a changé, ce qui affecte la taille de votre conteneur, vous devez donc mettre à jour votre taille). La réponse de Ben Alpert n'aide que les événements de redimensionnement de la fenêtre du navigateur. Voir ici: github.com/digidem/react-dimensions/issues/4
MindJuice
1
react-dimensionsgère les changements de taille de la fenêtre, les changements de disposition de la boîte flexible et les changements dus au redimensionnement JavaScript. Je pense que cela le couvre. Avez-vous un exemple qu'il ne gère pas correctement?
MindJuice
2
La taille de la fenêtre est triviale à obtenir. Pas besoin de bibliothèque pour ça: window.innerWidth, window.innerHeight. react-dimensionsrésout la partie la plus importante du problème et déclenche également votre code de mise en page lorsque la fenêtre est redimensionnée (ainsi que lorsque la taille du conteneur change).
MindJuice
1
Ce projet n'est plus maintenu activement.
Parabolord
7

Ce code utilise la nouvelle API de contexte React :

  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>;
    }
  }

Ensuite, vous pouvez l'utiliser où vous le souhaitez dans votre code comme ceci:

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

Vous n'avez pas nécessairement besoin de forcer un nouveau rendu.

Cela pourrait ne pas aider OP, mais dans mon cas, je n'avais besoin que de mettre à jour les attributs widthet heightsur ma toile (ce que vous ne pouvez pas faire avec CSS).

Cela ressemble à ceci:

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
la source
5

Je ne sais pas si c'est la meilleure approche, mais ce qui a fonctionné pour moi a d'abord été de créer un magasin, je l'ai appelé WindowStore:

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;

Ensuite, j'ai utilisé ce magasin dans mes composants, un peu comme ceci:

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());
    }
}));

Pour info, ces fichiers ont été partiellement rognés.

David Sinclair
la source
1
L'état du magasin ne doit changer qu'en réponse à une action envoyée. Ce n'est certainement pas une bonne façon de procéder.
2016
2
@frostymarvelous en effet, peut-être mieux relocalisé dans le composant comme dans les autres réponses.
David Sinclair
@DavidSinclair Ou encore mieux, onresizepourrait envoyer un WindowResizedAction.
John Weisz
1
C'est exagéré.
Jason Rice
5

Je sais que cela a été répondu, mais je pensais juste partager ma solution car la meilleure réponse, bien que géniale, peut maintenant être un peu dépassée.

    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 })
    }

La seule différence est que je fais rebondir (uniquement toutes les 200 ms) les updateWindowDimensions sur l'événement resize pour augmenter un peu les performances, MAIS je ne les rebondis pas quand il est appelé sur ComponentDidMount.

Je trouvais que le rebounce le rendait assez lent à monter parfois si vous avez une situation où il monte souvent.

Juste une optimisation mineure mais j'espère que cela aide quelqu'un!

Matt Wills
la source
Où est la définition de la initUpdateWindowDimensionsméthode?
Matt
Il est défini dans le constructeur :) c'est juste updateWindowDimensions lié au composant mais sans anti-rebond.
Matt Wills
3

Juste pour améliorer la solution de @ senornestor à utiliser forceUpdateet la solution de @ gkri pour supprimer l' resizeécouteur d'événements lors du démontage du composant:

  1. n'oubliez pas d'accélérer (ou de rebondir) l'appel à redimensionner
  2. assurez-vous que bind(this)dans le constructeur
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>
    )
  }
}

Une autre méthode consiste à utiliser simplement un état "factice" au lieu de 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
la source
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();
};

Il suffit de définir la fonction d'événement de redimensionnement.

Mettez ensuite à jour la taille du rendu (canevas), attribuez un nouveau format d'image à la caméra.

Le démontage et le rappel est une solution folle à mon avis ....

ci-dessous est le support si nécessaire.

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

Je voulais partager cette chose assez cool que je viens de trouver en utilisant 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 renvoie true ou false si la fenêtre est supérieure ou inférieure à la valeur de largeur maximale spécifiée, cela signifie qu'il n'est pas nécessaire de limiter l'écouteur, car matchMedia ne se déclenche qu'une seule fois lorsque le booléen change.

Mon code peut facilement être ajusté pour inclure useStatepour enregistrer les retours booléens matchMedia, et l'utiliser pour rendre conditionnellement un composant, déclencher une action, etc.

Riley Brown
la source
1

J'ai dû le lier à «this» dans le constructeur pour le faire fonctionner avec la syntaxe de classe

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
la source
1

Merci à tous pour les réponses. Voici mon React + Recompose . Il s'agit d'une fonction d'ordre élevé qui inclut les propriétés windowHeightet windowWidthdu composant.

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
la source
1

https://github.com/renatorib/react-sizes est un HOC pour ce faire tout en conservant de bonnes performances.

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
la source
0

Pour cette raison, il vaut mieux utiliser ces données à partir de données de fichiers CSS ou JSON, puis avec ces données, définir un nouvel état avec this.state ({width: "some value", height: "some value"}); ou écrire du code qui utilise des données d'écran de largeur dans le travail autonome si vous souhaitez afficher des images réactives

Miodrag Trajanovic
la source