vista rerender no navegador redimensionar com Reagir

votos
157

Como posso me Reagir a re-processar a visão quando a janela do navegador é redimensionada?

fundo

Eu tenho alguns blocos que eu quero para o layout individualmente na página, porém eu também quero que eles atualizar quando a janela muda browser. O resultado final muito será algo como Ben Holland disposição Pinterest, mas escrito usando Reagir não apenas jQuery. Eu ainda sou uma maneira fora.

Código

Aqui está a minha 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')
)

Em seguida, tem o Blockcomponente (equivalente a um Pinno exemplo acima 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>
    );
  }
});

ea 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 janela do jQuery redimensionar? Se sim, onde?

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

Existe uma “Reaja” forma mais de fazer isso?

Publicado 25/09/2013 em 19:06
fonte usuário
Em outras línguas...                            


19 respostas

votos
263

Você pode ouvir em componentDidMount, algo como este componente que exibe apenas as dimensões da janela (como <span>1024 x 768</span>):

var WindowDimensions = React.createClass({
    render: function() {
        return <span>{this.state.width} x {this.state.height}</span>;
    },
    updateDimensions: function() {
        this.setState({width: $(window).width(), height: $(window).height()});
    },
    componentWillMount: function() {
        this.updateDimensions();
    },
    componentDidMount: function() {
        window.addEventListener("resize", this.updateDimensions);
    },
    componentWillUnmount: function() {
        window.removeEventListener("resize", this.updateDimensions);
    }
});
Respondeu 25/09/2013 em 19:21
fonte usuário

votos
3

Não tenho certeza se esta é a melhor abordagem, mas o que funcionou para mim foi pela primeira vez a criação de uma loja, eu o chamei 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 em meus componentes, um bocado como este:

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

FYI, esses arquivos foram parcialmente cortada.

Respondeu 19/06/2015 em 17:53
fonte usuário

votos
10

Vou tentar dar uma resposta genérica, que tem como alvo este problema específico, mas um problema mais geral também.

Se você não se preocupam com os efeitos colaterais libs, você pode simplesmente usar algo como Packery

Se você usar Flux, você pode criar uma loja que contêm as propriedades da janela para que você mantenha uma função de tornar puro, sem ter que consultar a janela objeto everytime.

Em outros casos onde você quer construir um site responsivo, mas você prefere Reagir estilos inline para consultas de mídia, ou quer o comportamento HTML / JS para mudar de acordo com a largura da janela, manter a leitura:

O que é Reagir contexto e por isso que eu falar sobre isso

Reagir contexto de um não está na API pública e permite passar propriedades para toda uma hierarquia de componentes.

Reagir contexto é particularmente útil para passar para todo seu aplicativo coisas que nunca muda (ele é usado por muitas estruturas de fluxo através de um mixin). Você pode usá-lo para armazenar invariantes empresariais de aplicativos (como o userId conectado, de modo que ele está disponível em todos os lugares).

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 re-processado e não é fácil fazê-lo, a melhor solução é muitas vezes para desmontar / remontar o aplicativo inteiro com o novo contexto. Lembre-se ForceUpdate não é recursiva .

Então, como você entender, o contexto é prático, mas há um impacto no desempenho quando ele muda, por isso prefiro não deve mudar com muita freqüência.

O que colocar no contexto

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

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

O idioma do usuário atual :

Isso não muda muito oftenly, e quando o faz, como o aplicativo inteiro é traduzido temos que voltar a tornar tudo: um muito bom usecase da mudança langage quente

As propriedades da janela

Largura e altura para não alterar frequentemente mas quando fazemos a nossa disposição eo comportamento pode ter que se adaptar. Para o layout às vezes é fácil de personalizar com mediaqueries CSS, mas às vezes não é e requer uma estrutura HTML diferente. Para o comportamento que você tem que lidar com isso com Javascript.

Você não quer voltar a tornar tudo em cada evento redimensionar, então você tem que Debounce os eventos de redimensionamento.

O que eu entendo do seu problema é que você quer saber quantos itens para exibir de acordo com a largura da tela. Então você tem primeiro de definir pontos de interrupção de resposta , e enumerar o número de diferentes tipos de layout que você pode ter.

Por exemplo:

  • Layout "1col", para a largura <= 600
  • Layout "2col", por 600 <largura <1000
  • Layout "3col", para 1000 <= largura

Em eventos de redimensionamento (filtrada por), você pode facilmente obter o tipo de layout atual consultando o objeto de janela.

Em seguida, você pode comparar o tipo de layout com o tipo de layout antigo, e se mudou, re-tornar o aplicativo com um novo contexto: isto permite evitar re-renderizar o aplicativo em tudo quando o usuário gatilho redimensionar eventos, mas na verdade o tipo de esquema não mudou, assim você só renderizar novamente quando necessário.

Uma vez que você tem isso, você pode simplesmente usar o tipo de layout dentro do seu aplicativo (acessível através do contexto) para que você possa personalizar as HTML, comportamento, classes CSS ... Você sabe o tipo de layout dentro da função Reagir tornar então isso significa que você pode seguramente escrever sites responsivos usando estilos inline, e não precisam mediaqueries em tudo.

Se você usar Flux, você pode usar uma loja em vez de reagir contexto, mas se o seu aplicativo tem um monte de componentes sensíveis talvez seja mais simples de usar contexto?

Respondeu 22/07/2015 em 09:09
fonte usuário

votos
90

@SophieAlpert é certo, um, eu só quero fornecer uma versão modificada de sua solução, sem jQuery , com base em esta 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);
    }
});
Respondeu 26/12/2015 em 18:42
fonte usuário

votos
6

Gostaria que ignorar todas as respostas acima e começar a usar a react-dimensionsordem superior Component.

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

Basta adicionar um simples importe uma chamada de 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
Respondeu 04/03/2016 em 05:52
fonte usuário

votos
28

Uma solução muito simples:

resize = () => this.forceUpdate()

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

componentWillUnmount() {
  window.removeEventListener('resize', this.resize)
}
Respondeu 21/06/2016 em 18:19
fonte usuário

votos
20

É um exemplo simples e curta de usar ES6 sem 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>
    );
  }
}
Respondeu 28/07/2016 em 15:30
fonte usuário

votos
7

Eu uso @senornestor 's solução, mas para ser totalmente correto você tem que remover o ouvinte de eventos, bem como:

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

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

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

Caso contrário, você vai ter o aviso:

Aviso: ForceUpdate (...): Só pode atualizar um componente montado ou montagem. Isso geralmente significa que você chamou ForceUpdate () em um componente desmontado. Este é um não-op. Verifique o código para o componente XXX.

Respondeu 22/08/2016 em 13:29
fonte usuário

votos
1

Obrigado a todos pelas respostas. Aqui está a minha Reagir + Recompor . É uma função de ordem superior que inclui o windowHeighte windowWidthpropriedades para 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');
 }})
)
Respondeu 09/09/2017 em 14:30
fonte usuário

votos
0

Teve que vinculá-lo a 'this' no construtor para fazê-lo funcionar com a sintaxe 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)
  }
}
Respondeu 13/10/2017 em 03:52
fonte usuário

votos
3

Você não precisa necessariamente forçar uma re-render.

Isto pode não ajudá-OP, mas no meu caso eu só precisava atualizar o widthe heightatributos na minha tela (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;
`
Respondeu 13/11/2017 em 22:24
fonte usuário

votos
1

Eu sei que este foi respondida, mas apenas pensei que eu iria partilhar a minha solução como a resposta de topo, embora grande, agora pode ser um pouco desatualizado.

    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 eu estou debouncing (somente em execução a cada 200ms) os updateWindowDimensions no evento de redimensionamento para aumentar o desempenho um pouco, mas não debouncing-lo quando ele é chamado em ComponentDidMount.

I foi encontrar o debounce tornou bastante vagaroso para montar, por vezes, se você tem uma situação onde é a montagem muitas vezes.

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

Respondeu 05/01/2018 em 23:20
fonte usuário

votos
0

Por esta razão, é melhor se você usar esses dados do CSS ou arquivo JSON de dados e, em seguida, com esses dados definindo novo estado com this.state ({width: "algum valor", altura: "algum valor"}); ou escrevendo código que usam dados de dados da tela de largura em auto trabalho se desejar imagens mostram sensíveis

Respondeu 22/06/2018 em 18:49
fonte usuário

votos
1

Este código é usando o novo contexto Reagir 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() {
      const { children } = this.props;

      return <Provider value={this.state}>{children}</Provider>;
    }
  }

  export default WindowProvider;
  export const WindowConsumer = Consumer;

Em seguida, você pode usá-lo onde quiser em seu código como este:

<WindowConsumer>
  {{(width, height)} => //do what you want}
</WindowConsumer>
Respondeu 02/07/2018 em 10:22
fonte usuário

votos
1

https://github.com/renatorib/react-sizes é um HOC para fazer isso e ainda manter 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
Respondeu 26/12/2018 em 00:41
fonte usuário

votos
1
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ó precisa definir função de evento de redimensionamento.

Em seguida, atualize o tamanho renderers (lona), atribuir uma nova relação de aspecto para a câmera.

Desmontando e remouting é uma solução louco na minha opinião ....

abaixo é a montagem, se necessário.

            <div
                className={this.state.canvasActive ? 'canvasContainer isActive' : 'canvasContainer'}
                ref={mount => {
                    this.mount = mount;
                }}
            />
Respondeu 04/03/2019 em 10:20
fonte usuário

votos
0

A partir de Reagir 16.8 você pode usar ganchos !

/* 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}</>
}
Respondeu 24/04/2019 em 03:31
fonte usuário

votos
1

Apenas para melhorar em solução de @ senornestor de usar forceUpdatee solução de @ gkri para remover o resizeouvinte de evento em desmontar componente:

  1. não se esqueça de estrangular (ou debounce) a chamada para redimensionar
  2. certifique-se 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 "dummy" 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>
    )
  }
}
Respondeu 10/07/2019 em 05:14
fonte usuário

votos
0

Queria compartilhar essa coisa muito legal eu só encontrei 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 irá retornar verdadeiro ou falso se a janela é maior ou menor do que o valor máximo de largura especificada, isso significa que não há necessidade de estrangular o ouvinte, como o matchmedia só é acionado uma vez quando os boolean mudanças.

Meu código pode ser facilmente ajustado para incluir useStatepara salvar as matchmedia boolean retornos, e usá-lo para tornar condicionalmente uma ação componente, incêndio, etc.

Respondeu 12/07/2019 em 00:33
fonte usuário

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more