Vuelva a mostrar la vista en el cambio de tamaño del navegador con React


313

¿Cómo puedo hacer que React vuelva a representar la vista cuando se cambia el tamaño de la ventana del navegador?

Antecedentes

Tengo algunos bloques que quiero diseñar individualmente en la página, sin embargo, también quiero que se actualicen cuando cambie la ventana del navegador. El resultado final será algo así como el diseño de Pinterest de Ben Holland , pero escrito usando React no solo jQuery. Todavía estoy muy lejos.

Código

Aquí está mi aplicación:

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

Luego tengo el Blockcomponente (equivalente a a Pinen el ejemplo de Pinterest anterior):

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

y la lista / colección 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>
    );
  }
});

Pregunta

¿Debo agregar el cambio de tamaño de la ventana de jQuery? ¿Si es así, donde?

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

¿Hay una manera más "Reaccionar" de hacer esto?

Respuestas:


531

Usando React Hooks:

Puede definir un gancho personalizado que escuche el resizeevento de ventana , algo como esto:

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

La ventaja aquí es que la lógica está encapsulada, y puede usar este gancho en cualquier lugar que desee usar el tamaño de la ventana.

Usando las clases React:

Puede escuchar en componentDidMount, algo así como este componente que solo muestra las dimensiones de la ventana (como <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);
  }
}

55
¿Como funciona esto? El this.updateDimensionspasado a addEventListeneres solo una referencia de función desnuda que no tendrá valor para thiscuando se llama. ¿Debería usarse una función anónima, o una llamada .bind () para agregar this, o he entendido mal?
fadedbee

24
@chrisdew Llegué un poco tarde aquí, pero React se enlaza automáticamente thispara cualquier método definido directamente en el componente.
Michelle Tilley

3
@MattDell sí, las clases ES6 son solo clases normales, por lo que no se vincula automáticamente con ellas.
Michelle Tilley

30
No se necesita jQuery - use innerHeighty innerWidthfrom window. Y puede omitir componentWillMountsi usa getInitialStatepara configurar heighty width.
sighrobot

2
Miradas @MattDell como la ::sintaxis de BIND es por ahora sitepoint.com/bind-javascripts-this-keyword-react "el operador bind (: :) no va a ser parte de ES7, ES7 como el conjunto de características se congeló en febrero , el operador de enlace es una propuesta para ES8 "
Jarrod Smith

130

@SophieAlpert tiene razón, +1, solo quiero proporcionar una versión modificada de su solución, sin jQuery , basada en esta respuesta .

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

solo funciona cuando tienes tus polyfills relacionados con IE configurados para los oyentes de eventos vanilla JS
nnnn

@nnnn ¿puedes elaborar? Admito que solo probé esto en Chrome. ¿Estás diciendo que window.addEventListener no funcionará en IE sin polyfills?
André Pena

2
@andrerpena caniuse.com/#search=addeventlistener indica que ie8 tendría un problema
nnnn

2
@nnnn. Veo. Sí. Entonces mi solución no funciona en IE 8, pero funciona a partir de 9 :). Gracias.
André Pena

35
¿A alguien realmente le importa IE8? ¿O es solo un hábito?
frostymarvelous

48

Una solución muy simple:

resize = () => this.forceUpdate()

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

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

12
No olvides acelerar la actualización forzada o se verá muy fallida.
k2snowman69

44
¡Además, no olvides quitar el oyente componentWillUnmount()!
Jemar Jones

Estas son las mejores soluciones si se estrangula. Quiero decir, si forceUpdatese aplica condicionalmente
asmmahmud

1
No es la fuerza de actualización (la cosa que se llama) lo que necesita ser estrangulada, es la activación del evento de cambio de tamaño lo que necesita ser estrangulada (la cosa que se dispara). Técnicamente, los eventos de cambio de tamaño se pueden invocar en cada píxel al cambiar el tamaño de una ventana de mayor a menor. Cuando un usuario hace esto rápidamente, son más eventos de los que te interesan. Peor aún, está vinculando el hilo de la interfaz de usuario al hilo de Javascript, lo que significa que su aplicación comenzará a tener una sensación muy lenta a medida que intenta manejar cada evento individualmente.
k2snowman69

1
En lugar de ejecutar la función de cambio de tamaño en cada píxel, la ejecuta en un período de tiempo corto y periódico para dar la ilusión de fluidez, que siempre maneja el primer y último evento, dando la sensación de que está manejando el cambio de tamaño de manera fluida.
k2snowman69

42

Es un ejemplo simple y breve del uso de es6 sin 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>
    );
  }
}

manos

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

44
Esta es una respuesta agradable y sucinta, pero AFAICT tiene un error: a menos que me equivoque, el ::operador de enlace devuelve un nuevo valor cada vez que se aplica. Por lo tanto, su escucha de eventos en realidad no quedará sin registrar, ya que removeEventListenertermina pasando una función diferente a la que se le pasó originalmente addEventListener.
natevw

21

¡A partir de React 16.8 puedes usar Hooks !

/* 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}</>
}

13

Edit 2018 : ahora React tiene soporte de primera clase para el contexto


Trataré de dar una respuesta genérica, que aborde este problema específico pero también un problema más general.

Si no le importan las bibliotecas de efectos secundarios, simplemente puede usar algo como Packery

Si usa Flux, podría crear una tienda que contenga las propiedades de la ventana para mantener una función de renderización pura sin tener que consultar el objeto de la ventana cada vez.

En otros casos en los que desea crear un sitio web receptivo pero prefiere Reaccionar estilos en línea a consultas de medios, o desea que el comportamiento de HTML / JS cambie de acuerdo con el ancho de la ventana, siga leyendo:

¿Qué es el contexto React y por qué hablo?

El contexto de reacción no está en la API pública y permite pasar propiedades a toda una jerarquía de componentes.

El contexto de reacción es particularmente útil para pasar a toda su aplicación cosas que nunca cambian (es utilizado por muchos frameworks Flux a través de un mixin). Puede usarlo para almacenar invariantes comerciales de aplicaciones (como el ID de usuario conectado, para que esté disponible en todas partes).

Pero también se puede usar para almacenar cosas que pueden cambiar. El problema es que cuando el contexto cambia, todos los componentes que lo usan deben volverse a renderizar y no es fácil hacerlo, la mejor solución a menudo es desmontar / volver a montar toda la aplicación con el nuevo contexto. Recuerde forceUpdate no es recursivo .

Entonces, como comprende, el contexto es práctico, pero hay un impacto en el rendimiento cuando cambia, por lo que no debería cambiar con demasiada frecuencia.

Qué poner en contexto

  • Invariantes: como el ID de usuario conectado, sessionToken, lo que sea ...
  • Cosas que no cambian a menudo

Aquí hay cosas que no cambian a menudo:

El idioma del usuario actual :

No cambia muy a menudo, y cuando lo hace, a medida que se traduce toda la aplicación, tenemos que volver a renderizar todo: un caso muy útil de cambio de idioma.

Las propiedades de la ventana

El ancho y la altura no cambian con frecuencia, pero cuando lo hacemos, nuestro diseño y comportamiento pueden tener que adaptarse. Para el diseño, a veces es fácil de personalizar con CSS mediaqueries, pero a veces no lo es y requiere una estructura HTML diferente. Para el comportamiento tienes que manejar esto con Javascript.

No desea volver a renderizar todo en cada evento de cambio de tamaño, por lo que debe eliminar los eventos de cambio de tamaño.

Lo que entiendo de su problema es que desea saber cuántos elementos mostrar de acuerdo con el ancho de la pantalla. Por lo tanto, primero debe definir puntos de interrupción receptivos y enumerar el número de diferentes tipos de diseño que puede tener.

Por ejemplo:

  • Diseño "1col", para ancho <= 600
  • Diseño "2col", para 600 <ancho <1000
  • Diseño "3col", para 1000 <= ancho

En eventos de cambio de tamaño (sin rebote), puede obtener fácilmente el tipo de diseño actual consultando el objeto de la ventana.

Luego puede comparar el tipo de diseño con el tipo de diseño anterior, y si ha cambiado, vuelva a renderizar la aplicación con un nuevo contexto: esto permite evitar volver a renderizar la aplicación cuando el usuario ha activado eventos de cambio de tamaño, pero en realidad el el tipo de diseño no ha cambiado, por lo que solo se vuelve a generar cuando es necesario.

Una vez que tenga eso, simplemente puede usar el tipo de diseño dentro de su aplicación (accesible a través del contexto) para que pueda personalizar el HTML, el comportamiento, las clases de CSS ... Conozca su tipo de diseño dentro de la función React render, por lo que esto significa que puede escribir sitios web receptivos de forma segura mediante el uso de estilos en línea, y no necesita consultas multimedia en absoluto.

Si usa Flux, puede usar una tienda en lugar de React context, pero si su aplicación tiene muchos componentes receptivos, ¿tal vez sea más simple usar context?


10

Uso la solución de @senornestor, pero para ser completamente correcto, también debes eliminar el detector de eventos:

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

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

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

De lo contrario, recibirá la advertencia:

Advertencia: forceUpdate (...): solo puede actualizar un componente montado o montado. Esto generalmente significa que llamó a forceUpdate () en un componente desmontado. Este es un no-op. Verifique el código para el componente XXX.


1
Recibes la advertencia por una razón: lo que estás haciendo es una mala práctica. Su función render () debe ser una función pura de accesorios y estado. En su caso, debe guardar el nuevo tamaño en el estado.
zoran404

7

Me saltaría todas las respuestas anteriores y comenzaría a usar el react-dimensionsComponente de orden superior.

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

Simplemente agregue una importllamada simple y una función, y puede acceder this.props.containerWidthy this.props.containerHeighten su componente.

// 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

1
Esto no da el tamaño de la ventana, solo el contenedor.
mjtamlyn

3
Sí, ese es el punto. El tamaño de la ventana es trivial de encontrar. El tamaño de un contenedor es mucho más difícil de encontrar y mucho más útil para un componente React. La última versión de react-dimensionsincluso funciona para dimensiones cambiadas programáticamente (por ejemplo, el tamaño de un div cambiado, que afecta el tamaño de su contenedor, por lo que debe actualizar su tamaño). La respuesta de Ben Alpert solo ayuda en los eventos de cambio de tamaño de la ventana del navegador. Ver aquí: github.com/digidem/react-dimensions/issues/4
MindJuice

1
react-dimensionsmaneja cambios en el tamaño de la ventana, cambios en el diseño de flexbox y cambios debido al cambio de tamaño de JavaScript. Creo que eso lo cubre. ¿Tienes un ejemplo de que no se maneja correctamente?
MindJuice

2
El tamaño de la ventana es trivial. No hay necesidad de una biblioteca para eso: window.innerWidth, window.innerHeight. react-dimensionsresuelve la parte más importante del problema y también activa su código de diseño cuando se cambia el tamaño de la ventana (así como cuando cambia el tamaño del contenedor).
MindJuice

1
Este proyecto ya no se mantiene activamente.
Parabolord

7

Este código está utilizando la nueva API de contexto 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>;
    }
  }

Luego puede usarlo donde quiera en su código de esta manera:

<WindowConsumer>
  {({ width, height }) =>  //do what you want}
</WindowConsumer>

6

No necesariamente necesita forzar una nueva representación.

Puede que esto no ayude a OP, pero en mi caso solo necesitaba actualizar los atributos widthy heighten mi lienzo (que no puede hacer con CSS).

Se parece a esto:

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

5

No estoy seguro si este es el mejor enfoque, pero lo que funcionó para mí fue crear primero una Tienda, la llamé 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;

Luego usé esa tienda en mis componentes, algo así:

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

Para su información, estos archivos se recortaron parcialmente.


1
El estado de la tienda solo debe cambiar en respuesta a una acción despachada. Definitivamente, esta no es una buena manera de hacer esto.
frostymarvelous

2
@frostymarvelous de hecho, quizás mejor reubicado en el componente como en las otras respuestas.
David Sinclair

@DavidSinclair O incluso mejor, onresizepodría enviar a WindowResizedAction.
John Weisz

1
Esto es exagerado.
Jason Rice

5

Sé que esto ha sido respondido, pero pensé que compartiría mi solución como la respuesta principal, aunque excelente, ahora puede estar un poco desactualizado.

    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 única diferencia realmente es que estoy eliminando (solo ejecutando cada 200 ms) el updateWindowDimensions en el evento de cambio de tamaño para aumentar un poco el rendimiento, PERO no lo elimino cuando se llama en ComponentDidMount.

Descubrí que el rebote hace que sea bastante lento montarlo a veces si tienes una situación en la que se está montando a menudo.

¡Solo una pequeña optimización, pero espero que ayude a alguien!


¿Dónde está la definición del initUpdateWindowDimensionsmétodo?
Matt

Está definido en el constructor :) es solo updateWindowDimensions vinculado al componente pero sin el rebote.
Matt Wills

3

Solo para mejorar la solución de @ senornestor para usar forceUpdatey la solución de @ gkri para eliminar el resizedetector de eventos en el desmontaje del componente:

  1. no olvides estrangular (o cancelar) la llamada para cambiar el tamaño
  2. asegúrese de hacerlo bind(this)en el constructor
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>
    )
  }
}

Otro método es usar un estado "ficticio" en lugar 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>
    )
  }
}

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

Solo es necesario definir la función de cambio de tamaño del evento.

Luego actualice el tamaño de los renderizadores (lienzo), asigne una nueva relación de aspecto para la cámara.

Desmontar y enrutar es una solución loca en mi opinión ...

a continuación se muestra el montaje si es necesario.

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

2

Quería compartir esta cosa genial que encontré usando 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 devolverá verdadero o falso si la ventana es más alta o más baja que el valor de ancho máximo especificado, esto significa que no hay necesidad de estrangular al oyente, ya que matchMedia solo se dispara una vez cuando cambia el valor booleano.

Mi código se puede ajustar fácilmente para incluir useStatepara guardar los retornos booleanos de matchMedia y usarlo para representar condicionalmente un componente, acción de disparo, etc.


1

Tuve que vincularlo a 'this' en el constructor para que funcione con la sintaxis de Class

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

1

Gracias a todos por las respuestas. Aquí está mi React + Recompose . Es una función de orden superior que incluye el windowHeighty windowWidthpropiedades al componente.

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

1

https://github.com/renatorib/react-sizes es un HOC para hacer esto mientras se mantiene un buen rendimiento.

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

0

Por esta razón, es mejor si usa estos datos de archivos CSS o JSON, y luego con estos datos estableciendo un nuevo estado con this.state ({ancho: "algún valor", altura: "algún valor"}); o escribir código que use datos de datos de pantalla de ancho en trabajo propio si desea mostrar imágenes receptivas

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.