Renderize novamente a exibição no redimensionamento do navegador com o React


313

Como faço para o React renderizar novamente a exibição quando a janela do navegador é redimensionada?

fundo

Eu tenho alguns blocos que quero fazer o layout individualmente na página, mas também quero que eles sejam atualizados quando a janela do navegador mudar. O resultado final será algo como o layout do Pinterest de Ben Holland , mas escrito usando o React e não apenas o jQuery. Ainda estou longe.

Código

Aqui está o meu aplicativo:

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

Então eu tenho o Blockcomponente (equivalente a um Pinno exemplo acima do Pinterest):

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

e a lista / coleção 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>
    );
  }
});

Questão

Devo adicionar o redimensionamento da janela do jQuery? Se sim, onde?

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

Existe uma maneira mais "reagir" de fazer isso?

Respostas:


531

Usando ganchos de reação:

Você pode definir um gancho personalizado que escute o resizeevento da janela , algo como isto:

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

A vantagem aqui é que a lógica é encapsulada e você pode usar esse gancho em qualquer lugar que desejar usar o tamanho da janela.

Usando classes React:

Você pode ouvir em componentDidMount, algo como este componente que apenas exibe as dimensões da janela (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);
  }
}

5
Como é que isso funciona? O this.updateDimensionspassado para addEventListeneré apenas uma referência de função simples que não terá valor para thisquando chamado. Uma função anônima ou uma chamada .bind () deve ser usada para adicionar thisou entendi mal?
Fadedbee

24
@chrisdew Estou um pouco atrasado aqui, mas React auto-binds thispara qualquer método definido diretamente no componente.
precisa

3
@ MattDell sim, as classes ES6 são apenas classes normais, portanto, não há vinculação automática com elas.
precisa saber é o seguinte

30
Não é necessário jQuery - use innerHeighte innerWidthde window. E você pode pular componentWillMountse usar getInitialStatepara definir heighte width.
sighrobot

2
@MattDell parece que a ::sintaxe de ligação não está disponível no momento sitepoint.com/bind-javascripts-this-keyword-react "o operador de ligação (: :) não fará parte do ES7, pois o conjunto de recursos do ES7 foi congelado em fevereiro , o operador de ligação é uma proposta para ES8 "
Jarrod Smith

130

@SophieAlpert está certa, +1, eu apenas quero fornecer uma versão modificada de sua solução, sem jQuery , com base nesta resposta .

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

só funciona quando você tem seus polyfills relacionados com o IE configurado para ouvintes de eventos de baunilha JS
nnnn

@nnnn você pode elaborar? Admito que só testei isso no Chrome. Você está dizendo que o window.addEventListener não funciona no IE sem polyfills?
André Pena

2
@andrerpena caniuse.com/#search=addeventlistener indica que ie8 teria um problema
nnnn

2
@nnnn. Entendo. Sim .. Portanto, minha solução não funciona no IE 8, mas funciona a partir das 9 em :). Obrigado.
André Pena

35
Alguém ainda se importa com o IE8? Ou é apenas hábito?
Frostymarvelous

48

Uma solução muito simples:

resize = () => this.forceUpdate()

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

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

12
Não se esqueça de acelerar a atualização de força ou ela parecerá muito complicada.
k2snowman69

4
Também não se esqueça de remover o ouvinte componentWillUnmount()!
Jemar Jones

Esta é a melhor solução se for regulada. Quero dizer, se o forceUpdateé aplicado condicionalmente
asmmahmud

1
Não é a forceUpdate (a coisa que está sendo chamada) que precisa ser regulada, é o disparo do evento de redimensionamento que precisa ser regulado (a coisa que está sendo acionada). Tecnicamente, os eventos de redimensionamento podem ser chamados a cada pixel à medida que você redimensiona uma janela de grande para pequena. Quando um usuário faz isso rapidamente, há mais eventos do que você gosta. Pior ainda, você vincula o segmento da interface do usuário ao segmento Javascript, o que significa que seu aplicativo começará a ter uma sensação muito lenta enquanto tenta lidar com cada evento individualmente.
K2snowman69

1
Em vez de executar a função de redimensionamento a cada pixel, você a executa em um curto período de tempo periódico para dar a ilusão de fluidez que sempre lida com o primeiro e o último evento, dando a sensação de que ele está lidando com o redimensionamento com fluidez.
K2snowman69

42

É um exemplo simples e curto de uso do es6 sem o 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>
    );
  }
}

ganchos

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

4
Esta é uma resposta agradável e sucinta, mas o AFAICT possui um erro: a menos que eu esteja enganado, o ::operador de ligação retornará um novo valor cada vez que for aplicado. Portanto, seu ouvinte de evento não será realmente registrado, pois você removeEventListeneracaba passando uma função diferente da que foi originalmente transmitida addEventListener.
Natevw

21

A partir do React 16.8, você pode 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 : now React tem suporte de primeira classe para o contexto


Vou tentar dar uma resposta genérica, que atinja esse problema específico, mas também um problema mais geral.

Se você não se importa com as bibliotecas de efeitos colaterais, pode simplesmente usar algo como Packery

Se você usar o Flux, poderá criar um armazenamento que contenha as propriedades da janela para manter uma função de renderização pura sem precisar consultar o objeto da janela toda vez.

Em outros casos em que você deseja criar um site responsivo, mas prefere Reagir estilos embutidos a consultas de mídia ou deseja que o comportamento HTML / JS mude de acordo com a largura da janela, continue lendo:

O que é o contexto React e por que eu falo sobre isso

O contexto de reação an não está na API pública e permite passar propriedades para toda uma hierarquia de componentes.

O contexto de reação é particularmente útil para passar para todo o aplicativo coisas que nunca mudam (é usado por muitas estruturas do Flux através de um mixin). Você pode usá-lo para armazenar invariantes de negócios de aplicativos (como o userId conectado, para que esteja disponível em qualquer lugar).

Mas também pode ser usado para armazenar coisas que podem mudar. O problema é que, quando o contexto muda, todos os componentes que o utilizam devem ser renderizados novamente e não é fácil fazê-lo, a melhor solução é desmontar / remontar o aplicativo inteiro com o novo contexto. Lembre-se de forceUpdate não é recursivo .

Então, como você entende, o contexto é prático, mas há um impacto no desempenho quando muda, portanto, não deve mudar com muita frequência.

O que colocar no contexto

  • Invariantes: como o userId conectado, sessionToken, qualquer que seja ...
  • Coisas que não mudam frequentemente

Aqui estão as coisas que não mudam frequentemente:

O idioma atual do usuário :

Ele não muda com muita frequência e, quando muda, como todo o aplicativo é traduzido, precisamos renderizar tudo novamente: uma ótima maneira de mudar de idioma

As propriedades da janela

A largura e a altura não mudam frequentemente, mas quando fazemos nosso layout e comportamento, talvez seja necessário adaptar-se. Às vezes, para o layout, é fácil personalizar com mediaqueries CSS, mas às vezes não é e requer uma estrutura HTML diferente. Para o comportamento, você deve lidar com isso com Javascript.

Você não deseja renderizar tudo novamente em todos os eventos de redimensionamento, portanto, é necessário renunciar aos eventos de redimensionamento.

O que eu entendo do seu problema é que você deseja saber quantos itens serão exibidos de acordo com a largura da tela. Portanto, você deve primeiro definir pontos de interrupção responsivos e enumerar o número de diferentes tipos de layout que você pode ter.

Por exemplo:

  • Esquema "1col", para largura <= 600
  • Esquema "2col", para 600 <largura <1000
  • Esquema "3col", para 1000 <= largura

Nos eventos de redimensionamento (rejeitados), é possível obter facilmente o tipo de layout atual consultando o objeto da janela.

Em seguida, você pode comparar o tipo de layout com o tipo de layout anterior e, se ele foi alterado, renderize novamente o aplicativo com um novo contexto: isso permite evitar a renderização novamente do aplicativo quando o usuário acionar eventos de redimensionamento, mas na verdade o o tipo de layout não foi alterado, portanto, você só renderiza novamente quando necessário.

Depois disso, você pode simplesmente usar o tipo de layout dentro do seu aplicativo (acessível através do contexto) para personalizar o HTML, o comportamento, as classes CSS ... Você conhece o seu tipo de layout na função React render, para que você possa pode escrever sites responsivos com segurança usando estilos em linha e não precisa de consultas de mídia.

Se você usa o Flux, pode usar uma loja em vez do contexto React, mas se seu aplicativo tiver muitos componentes responsivos, talvez seja mais fácil usar o contexto?


10

Eu uso a solução do @senornestor, mas, para estar totalmente correto, você também precisa remover o ouvinte de evento:

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

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

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

Caso contrário, você receberá o aviso:

Aviso: forceUpdate (...): pode atualizar apenas um componente montado ou montado. Isso geralmente significa que você chamou forceUpdate () em um componente desmontado. Este é um não-op. Por favor, verifique o código do componente XXX.


1
Você está recebendo o aviso por um motivo: o que está fazendo é uma má prática. Sua função render () deve ser uma função pura de adereços e estado. No seu caso, você deve salvar o novo tamanho no estado.
Zoran404

7

Eu pularia todas as respostas acima e começaria a usar o react-dimensionscomponente de ordem superior.

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

Basta adicionar uma importchamada simples e uma função, e você pode acessar this.props.containerWidthe this.props.containerHeightem seu 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
Isso não fornece o tamanho da janela, apenas o contêiner.
Mjtamlyn # 10/16

3
Sim, esse é o ponto. O tamanho da janela é trivial de encontrar. O tamanho de um contêiner é muito mais difícil de encontrar e muito mais útil para um componente React. A versão mais recente do react-dimensionseven funciona para dimensões alteradas programaticamente (por exemplo, o tamanho de uma div alterado, o que afeta o tamanho do seu contêiner, portanto, é necessário atualizar o tamanho). A resposta de Ben Alpert ajuda apenas na janela do navegador a redimensionar eventos. Veja aqui: github.com/digidem/react-dimensions/issues/4
MindJuice

1
react-dimensionslida com alterações no tamanho da janela, alterações no layout da caixa flexível e alterações devido ao redimensionamento do JavaScript. Eu acredito que isso é suficiente. Você tem um exemplo que ele não lida corretamente?
#

2
O tamanho da janela é fácil de obter. Não há necessidade de uma biblioteca para isso: window.innerWidth, window.innerHeight. react-dimensionsresolve a parte mais importante do problema e também aciona o código do layout quando a janela é redimensionada (assim como quando o tamanho do contêiner é alterado).
mindjuice

1
Este projeto não está mais sendo mantido ativamente.
Parabolord

7

Este código está usando a nova API de contexto do 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>;
    }
  }

Em seguida, você pode usá-lo onde quiser no seu código assim:

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

6

Você não precisa necessariamente forçar uma nova renderização.

Isso pode não ajudar o OP, mas, no meu caso, eu só precisava atualizar os atributos widthe heightna minha tela (o que você não pode fazer com CSS).

Se parece com isso:

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

Não tenho certeza se essa é a melhor abordagem, mas o que funcionou para mim foi a criação de uma loja, chamei de 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;

Então eu usei essa loja nos meus componentes, mais ou menos assim:

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 sua informação, esses arquivos foram parcialmente cortados.


1
O estado da loja deve mudar apenas em resposta a uma ação despachada. Definitivamente, essa não é uma boa maneira de fazer isso.
Frostymarvelous

2
@frostymarvelous de fato, talvez seja melhor realocado no componente como nas outras respostas.
David Sinclair

@DavidSinclair Ou melhor ainda, onresizepoderia enviar a WindowResizedAction.
31416 John Weisz

1
Isso é um exagero.
Jason Rice

5

Sei que isso foi respondido, mas pensei em compartilhar minha solução, pois a resposta principal, embora ótima, agora pode estar um pouco desatualizada.

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

A única diferença realmente é que estou denunciando (executando apenas a cada 200ms) o updateWindowDimensions no evento redimensionar para aumentar um pouco o desempenho, MAS não o denuncio quando é chamado em ComponentDidMount.

Eu estava descobrindo que o rebounce tornava bastante lento a montagem às vezes, se você tem uma situação em que ela está montando com frequência.

Apenas uma pequena otimização, mas espero que ajude alguém!


Onde está a definição do initUpdateWindowDimensionsmétodo?
Matt

É definido no construtor :) é apenas updateWindowDimensions vinculado ao componente, mas sem o debounce.
Matt Wills

3

Apenas para melhorar a solução do @ senornestor a ser usada forceUpdatee a solução do @ gkri para remover o resizeouvinte de eventos na desmontagem de componentes:

  1. não se esqueça de limitar (ou rejeitar) a chamada para redimensionar
  2. certifique-se de bind(this)no construtor
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>
    )
  }
}

Outro método é usar apenas um estado "fictício" em vez 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();
};

Só é necessário definir a função de evento de redimensionamento.

Atualize o tamanho dos renderizadores (tela), atribua uma nova proporção para a câmera.

Desmontar e remontar é uma solução louca na minha opinião ....

abaixo está o suporte, se necessário.

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

2

Queria compartilhar essa coisa bem legal que acabei de encontrar 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 retornará true ou false se a janela for maior ou menor que o valor de largura máxima especificado, isso significa que não há necessidade de limitar o ouvinte, pois o matchMedia é acionado apenas uma vez quando o booleano for alterado.

Meu código pode ser facilmente ajustado para incluir useStatepara salvar os retornos booleanos matchMedia e usá-lo para renderizar condicionalmente um componente, ação de disparo etc.


1

Tinha que vinculá-lo a 'this' no construtor para fazê-lo funcionar com a sintaxe da 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)
  }
}

1

Obrigado a todos pelas respostas. Aqui está o meu React + Recompose . É uma função de alta ordem que inclui as propriedades windowHeighte windowWidthpara o 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 é um HOC para fazer isso, mantendo um bom desempenho.

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 esse motivo, é melhor se você usar esses dados dos dados do arquivo CSS ou JSON e, com esses dados, definir um novo estado com this.state ({width: "some value", height: "some value"}); ou escrever código que use dados de dados da tela de largura no trabalho por conta própria, se desejar exibir imagens responsivas

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.