Visualizzazione del rendering sul ridimensionamento del browser con React


313

Come posso ottenere React per eseguire nuovamente il rendering della vista quando la finestra del browser viene ridimensionata?

sfondo

Ho alcuni blocchi che voglio layout individualmente sulla pagina, tuttavia voglio anche che vengano aggiornati quando cambia la finestra del browser. Il risultato finale sarà qualcosa di simile al layout Pinterest di Ben Holland , ma scritto usando React non solo jQuery. Sono ancora lontana.

Codice

Ecco la mia 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')
)

Quindi ho il Blockcomponente (equivalente a un Pinnell'esempio Pinterest sopra):

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 l'elenco / raccolta di 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>
    );
  }
});

Domanda

Devo aggiungere il ridimensionamento della finestra di jQuery? Se sì, dove?

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

Esiste un modo più "reattivo" per farlo?

Risposte:


531

Usando React Hook:

È possibile definire un hook personalizzato che ascolta l' resizeevento window , in questo modo:

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

Il vantaggio qui è che la logica è incapsulata e puoi usare questo hook ovunque tu voglia usare la dimensione della finestra.

Utilizzando le classi React:

Puoi ascoltare in componentDidMount, qualcosa come questo componente che mostra solo le dimensioni della finestra (come <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
Come funziona? Il this.updateDimensionspassato a addEventListenerè solo un riferimento di funzione nuda che non avrà alcun valore per thisquando viene chiamato. Per aggiungere una funzione anonima o una chiamata .bind () thiso ho frainteso?
fadedbee,

24
@chrisdew Sono un po 'in ritardo qui, ma React si associa automaticamente thisa tutti i metodi definiti direttamente sul componente.
Michelle Tilley,

3
@MattDell sì, le classi ES6 sono solo normali, quindi nessuna auto-associazione con esse.
Michelle Tilley,

30
Non è necessario jQuery: utilizzare innerHeighte innerWidthda window. E puoi saltare componentWillMountse usi getInitialStateper impostare heighte width.
sighrobot,

2
@MattDell sembra che ::per ora la sintassi del bind non sia disponibile sitepoint.com/bind-javascripts-this-keyword-react "l'operatore bind (: :) non farà parte di ES7, poiché le funzionalità di ES7 sono state bloccate a febbraio , l'operatore di bind è una proposta per ES8 "
Jarrod Smith

130

@SophieAlpert ha ragione, +1, voglio solo fornire una versione modificata della sua soluzione, senza jQuery , basata su questa risposta .

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

Funziona solo quando hai configurato i tuoi polyfill per IE per i listener di eventi JS vanilla
nnnn

@nnnn puoi elaborare? Ammetto di averlo testato solo su Chrome. Stai dicendo che window.addEventListener non funzionerà su IE senza polyfill?
André Pena,

2
@andrerpena caniuse.com/#search=addeventlistener indica che ie8 avrebbe un problema
nnnn

2
@nnnn. Vedo. Sì .. Quindi la mia soluzione non funziona su IE 8, ma funziona dalle 9 in poi :). Grazie.
André Pena,

35
Qualcuno si preoccupa ancora di IE8? O è solo un'abitudine?
gelido

48

Una soluzione molto semplice:

resize = () => this.forceUpdate()

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

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

12
Non dimenticare di limitare l'aggiornamento della forza o sembrerà molto glitch.
k2snowman69,

4
Inoltre, non dimenticare di rimuovere l'ascoltatore componentWillUnmount()!
Jemar Jones,

Questa è la soluzione migliore se è limitata. Voglio dire se forceUpdateviene applicato in modo condizionale
asmmahmud,

1
Non è il forceUpdate (la cosa che viene chiamata) che deve essere strozzato, è il licenziamento dell'evento di ridimensionamento che deve essere limitato (il fuoco della cosa). Gli eventi di ridimensionamento possono essere chiamati tecnicamente su ogni pixel quando si ridimensiona una finestra da grande a piccola. Quando un utente lo fa rapidamente, sono più eventi di quelli a cui tieni. Peggio ancora, stai collegando il thread dell'interfaccia utente al thread Javascript, il che significa che la tua app inizierà a sentirsi seriamente lenta mentre cerca di gestire quale singolo evento individualmente.
k2snowman69

1
Invece di eseguire la funzione di ridimensionamento su ogni pixel, la si esegue in un breve intervallo di tempo periodico per dare l'illusione di fluidità che si gestisce sempre il primo e l'ultimo evento dando così la sensazione che stia gestendo fluidamente il ridimensionamento.
k2snowman69,

42

È un semplice e breve esempio dell'uso di es6 senza 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>
    );
  }
}

ganci

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
Questa è una risposta simpatica e concisa, ma AFAICT ha un bug: a meno che non mi sbagli, l' ::operatore di bind restituisce un nuovo valore ogni volta che viene applicato. Quindi il tuo listener di eventi non verrà effettivamente non registrato, poiché removeEventListenerfinirai per passare una funzione diversa da quella originariamente passata addEventListener.
natevw,

21

A partire da React 16.8 puoi usare 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

Modifica 2018 : ora React ha un supporto di prima classe per il contesto


Cercherò di dare una risposta generica, che affronti questo problema specifico ma anche un problema più generale.

Se non ti interessano le librerie di effetti collaterali, puoi semplicemente usare qualcosa come Packery

Se si utilizza Flux, è possibile creare un negozio che contenga le proprietà della finestra in modo da mantenere una funzione di rendering pura senza dover interrogare l'oggetto finestra ogni volta.

In altri casi in cui desideri creare un sito Web reattivo ma preferisci gli stili inline React alle query multimediali o desideri che il comportamento HTML / JS cambi in base alla larghezza della finestra, continua a leggere:

Qual è il contesto di React e perché ne parlo

Il contesto di reazione an non si trova nell'API pubblica e consente di passare le proprietà a un'intera gerarchia di componenti.

Il contesto React è particolarmente utile per passare a tutta la tua app cose che non cambiano mai (viene utilizzato da molti framework Flux attraverso un mixin). Puoi usarlo per archiviare gli invarianti aziendali di app (come l'ID utente collegato, in modo che sia disponibile ovunque).

Ma può anche essere usato per conservare cose che possono cambiare. Il problema è che quando il contesto cambia, tutti i componenti che lo usano dovrebbero essere ridistribuiti e non è facile farlo, la soluzione migliore è spesso smontare / rimontare l'intera app con il nuovo contesto. Ricorda forceUpdate non è ricorsivo .

Quindi, come capisci, il contesto è pratico, ma ha un impatto sulle prestazioni quando cambia, quindi non dovrebbe cambiare troppo spesso.

Cosa mettere in contesto

  • Invarianti: come l'utente collegato ID, sessionToken, qualunque cosa ...
  • Cose che non cambiano spesso

Ecco cose che non cambiano spesso:

L'attuale lingua dell'utente :

Non cambia molto spesso, e quando lo fa, mentre l'intera app viene tradotta dobbiamo ri-renderizzare tutto: un bel caso di cambio di lingue calde

Le proprietà della finestra

La larghezza e l'altezza non cambiano spesso, ma quando lo facciamo il nostro layout e comportamento potrebbero dover adattarsi. Per il layout a volte è facile personalizzare con mediaquery CSS, ma a volte non lo è e richiede una diversa struttura HTML. Per il comportamento devi gestirlo con Javascript.

Non vuoi ripetere il rendering di tutto su ogni evento di ridimensionamento, quindi devi rimbalzare gli eventi di ridimensionamento.

Quello che capisco del tuo problema è che vuoi sapere quanti elementi visualizzare in base alla larghezza dello schermo. Quindi devi prima definire i punti di interruzione reattivi ed enumerare il numero di diversi tipi di layout che puoi avere.

Per esempio:

  • Layout "1col", per larghezza <= 600
  • Layout "2col", per 600 <larghezza <1000
  • Layout "3col", per 1000 <= larghezza

Sugli eventi di ridimensionamento (annullati), è possibile ottenere facilmente il tipo di layout corrente eseguendo una query sull'oggetto finestra.

Quindi è possibile confrontare il tipo di layout con il precedente tipo di layout e, se è cambiato, eseguire nuovamente il rendering dell'app con un nuovo contesto: ciò consente di evitare di ripetere il rendering dell'app quando l'utente ha attivato eventi di ridimensionamento ma in realtà il il tipo di layout non è cambiato, quindi esegui nuovamente il rendering solo quando richiesto.

Una volta che hai quello, puoi semplicemente usare il tipo di layout all'interno della tua app (accessibile attraverso il contesto) in modo da poter personalizzare HTML, comportamento, classi CSS ... Conosci il tuo tipo di layout all'interno della funzione di rendering di React quindi questo significa che è in grado di scrivere siti Web responsive utilizzando stili incorporati e non necessita di mediaquery.

Se usi Flux, puoi usare un negozio invece del contesto React, ma se la tua app ha molti componenti reattivi forse è più semplice usare il contesto?


10

Uso la soluzione di @senornestor, ma per essere del tutto corretti è necessario rimuovere anche il listener di eventi:

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

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

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

Altrimenti riceverai l'avvertimento:

Avvertenza: forceUpdate (...): può aggiornare solo un componente montato o montato. Questo di solito significa che hai chiamato forceUpdate () su un componente non montato. Questa è una no-op. Controlla il codice per il componente XXX.


1
Ricevi un avviso per un motivo: quello che stai facendo è una cattiva pratica. La tua funzione render () dovrebbe essere una pura funzione di oggetti di scena e stato. Nel tuo caso dovresti salvare la nuova dimensione nello stato.
zoran404,

7

Vorrei saltare tutte le risposte sopra e iniziare a utilizzare il react-dimensionscomponente di ordine superiore.

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

Basta aggiungere una importchiamata semplice e una funzione e puoi accedere this.props.containerWidthe this.props.containerHeightnel tuo 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
Questo non indica la dimensione della finestra, ma solo il contenitore.
mjtamlyn,

3
Sì, questo è il punto. La dimensione della finestra è banale da trovare. La dimensione di un contenitore è molto più difficile da trovare e molto più utile per un componente React. L'ultima versione di react-dimensionsfunziona anche per dimensioni modificate a livello di codice (ad esempio, la dimensione di un div è cambiata, il che influisce sulla dimensione del contenitore, quindi è necessario aggiornare la dimensione). La risposta di Ben Alpert aiuta solo a ridimensionare gli eventi nella finestra del browser. Vedi qui: github.com/digidem/react-dimensions/issues/4
MindJuice

1
react-dimensionsgestisce le modifiche alle dimensioni della finestra, le modifiche al layout del flexbox e le modifiche dovute al ridimensionamento di JavaScript. Penso che lo copra. Hai un esempio che non gestisce correttamente?
MindJuice,

2
La dimensione della finestra è banale da ottenere. Non c'è bisogno di una biblioteca per questo: window.innerWidth, window.innerHeight. react-dimensionsrisolve la parte più importante del problema e attiva anche il codice del layout quando viene ridimensionata la finestra (nonché quando cambia la dimensione del contenitore).
MindJuice,

1
Questo progetto non viene più mantenuto attivamente.
Parabolord,

7

Questo codice utilizza la nuova API di contesto 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>;
    }
  }

Quindi puoi usarlo dove vuoi nel tuo codice in questo modo:

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

6

Non è necessario forzare necessariamente un nuovo rendering.

Questo potrebbe non aiutare OP, ma nel mio caso avevo solo bisogno di aggiornare gli attributi widthe heightsulla mia tela (cosa che non puoi fare con i CSS).

Sembra così:

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

Non sono sicuro che questo sia l'approccio migliore, ma ciò che ha funzionato per me è stata la prima creazione di un negozio, l'ho chiamato 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;

Quindi ho usato quel negozio nei miei componenti, un po 'così:

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

Cordiali saluti, questi file sono stati parzialmente tagliati.


1
Lo stato del punto vendita dovrebbe cambiare solo in risposta a un'azione inviata. Questo non è sicuramente un buon modo per farlo.
gelido il

2
@frostymarvelous davvero, forse meglio trasferito nel componente come nelle altre risposte.
David Sinclair,

@DavidSinclair O ancora meglio, onresizepotrebbe inviare a WindowResizedAction.
John Weisz,

1
Questo è eccessivo.
Jason Rice,

5

So che è stata data una risposta, ma ho pensato di condividere la mia soluzione poiché la risposta migliore, sebbene eccezionale, ora potrebbe essere un po 'datata.

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

L'unica differenza è che sto rimbalzando (eseguendo solo ogni 200 ms) updateWindowDimensions sull'evento di ridimensionamento per aumentare un po 'le prestazioni, MA non rimuoverlo quando viene chiamato su ComponentDidMount.

Stavo scoprendo che il debounce lo rendeva piuttosto lento da montare a volte se hai una situazione in cui sta montando spesso.

Solo un'ottimizzazione minore ma spero che aiuti qualcuno!


Dov'è la definizione del initUpdateWindowDimensionsmetodo?
Matt,

È definito nel costruttore :) è solo updateWindowDimensions associato al componente ma senza il debounce.
Matt Wills,

3

Solo per migliorare la soluzione da utilizzare di forceUpdate@ senornestor e la soluzione di @ gkri per rimuovere il resizelistener di eventi sullo smontaggio dei componenti:

  1. non dimenticare di limitare (o rimbalzare) la chiamata per ridimensionare
  2. assicurati di bind(this)nel costruttore
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>
    )
  }
}

Un altro metodo consiste nell'utilizzare uno stato "fittizio" anziché 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();
};

Devono solo definire la funzione di evento di ridimensionamento.

Quindi aggiorna le dimensioni del renderer (tela), assegna un nuovo formato per la telecamera.

Smontare e rimontare è una soluzione folle secondo me ....

sotto è il supporto, se necessario.

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

2

Volevo condividere questa cosa piuttosto bella che ho appena trovato 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 restituirà vero o falso se la finestra è superiore o inferiore al valore di larghezza massima specificato, ciò significa che non è necessario limitare l'ascoltatore, poiché matchMedia si attiva solo una volta quando il valore booleano cambia.

Il mio codice può essere facilmente modificato in modo da includere useStateper salvare i ritorni booleani matchMedia e utilizzarlo per eseguire il rendering condizionale di un componente, un'azione di attivazione, ecc.


1

Ho dovuto associarlo a "questo" nel costruttore per farlo funzionare con la sintassi della 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

Grazie a tutti per le risposte. Ecco il mio React + Ricomponi . È una funzione di ordine superiore che include le proprietà windowHeighte windowWidthper il 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 è un HOC per fare ciò pur mantenendo buone prestazioni.

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

Per questo motivo è meglio se si utilizzano questi dati dai dati dei file CSS o JSON e quindi con questi dati si imposta un nuovo stato con this.state ({larghezza: "un valore", altezza: "un valore"}); o scrivere codice che utilizza i dati dei dati della larghezza dello schermo in modo autonomo se si desidera visualizzare immagini dello spettacolo reattive

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.