Rerender view pada browser resize dengan React

313

Bagaimana saya bisa mendapatkan React untuk merender ulang tampilan ketika jendela browser diubah ukurannya?

Latar Belakang

Saya memiliki beberapa blok yang ingin saya tata letak satu per satu di halaman, namun saya juga ingin mereka memperbarui ketika jendela browser berubah. Hasil akhirnya akan seperti tata letak Pinterest Ben Holland , tetapi ditulis menggunakan React bukan hanya jQuery. Aku masih jauh.

Kode

Inilah aplikasi saya:

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

Lalu saya memiliki Blockkomponen (setara dengan Pindalam contoh Pinterest di atas):

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

dan daftar / koleksi 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>
    );
  }
});

Pertanyaan

Haruskah saya menambahkan ukuran jendela jQuery? Jika ya, dimana?

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

Apakah ada cara lain "Bereaksi" untuk melakukan ini?

digibake
sumber

Jawaban:

531

Menggunakan React Hooks:

Anda dapat menentukan Hook kustom yang mendengarkan resizeacara jendela , seperti ini:

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

Keuntungannya di sini adalah logik yang dirangkum, dan Anda dapat menggunakan Hook ini di mana pun Anda ingin menggunakan ukuran jendela.

Menggunakan kelas Bereaksi:

Anda dapat mendengarkan di componentDidMount, sesuatu seperti komponen ini yang hanya menampilkan dimensi jendela (seperti <span>Window size: 1024 x 768</span>):

import React from 'react';

class ShowWindowDimensions extends React.Component {
  state = { width: 0, height: 0 };
  render() {
    return <span>Window size: {this.state.width} x {this.state.height}</span>;
  }
  updateDimensions = () => {
    this.setState({ width: window.innerWidth, height: window.innerHeight });
  };
  componentDidMount() {
    window.addEventListener('resize', this.updateDimensions);
  }
  componentWillUnmount() {
    window.removeEventListener('resize', this.updateDimensions);
  }
}
Sophie Alpert
sumber
5
Bagaimana cara kerjanya? Yang this.updateDimensionsditeruskan ke addEventListenerhanyalah referensi fungsi kosong yang tidak akan memiliki nilai thisketika dipanggil. Haruskah fungsi anonim, atau panggilan .bind () digunakan untuk menambahkan this, atau apakah saya salah paham?
fadedbee
24
@ Chrisdew Saya sedikit terlambat di sini, tetapi Bereaksi otomatis-mengikat thisuntuk metode yang didefinisikan langsung pada komponen.
Michelle Tilley
3
@MattDell ya, kelas ES6 hanya kelas normal, jadi tidak ada ikatan otomatis dengan mereka.
Michelle Tilley
30
Tidak diperlukan jQuery - gunakan innerHeightdan innerWidthdari window. Dan Anda dapat melewati componentWillMountjika Anda gunakan getInitialStateuntuk mengatur heightdan width.
sighrobot
2
@MattDell sepertinya ::sintaks bind keluar untuk saat ini sitepoint.com/bind-javascripts-this-keyword-react "operator bind (: :) tidak akan menjadi bagian dari ES7, karena set fitur ES7 dibekukan pada bulan Februari , operator mengikat adalah proposal untuk ES8 "
Jarrod Smith
130

@SophieAlpert benar, +1, saya hanya ingin memberikan versi modifikasi dari solusinya, tanpa jQuery , berdasarkan jawaban ini .

var WindowDimensions = React.createClass({
    render: function() {
        return <span>{this.state.width} x {this.state.height}</span>;
    },
    updateDimensions: function() {

    var w = window,
        d = document,
        documentElement = d.documentElement,
        body = d.getElementsByTagName('body')[0],
        width = w.innerWidth || documentElement.clientWidth || body.clientWidth,
        height = w.innerHeight|| documentElement.clientHeight|| body.clientHeight;

        this.setState({width: width, height: height});
        // if you are using ES2015 I'm pretty sure you can do this: this.setState({width, height});
    },
    componentWillMount: function() {
        this.updateDimensions();
    },
    componentDidMount: function() {
        window.addEventListener("resize", this.updateDimensions);
    },
    componentWillUnmount: function() {
        window.removeEventListener("resize", this.updateDimensions);
    }
});
André Pena
sumber
hanya berfungsi saat Anda mengatur polyfill terkait IE untuk pendengar acara vanilla JS
nnnn
@nnnn bisakah kamu menjelaskan? Saya akui saya hanya menguji ini di Chrome. Apakah Anda mengatakan window.addEventListener tidak akan berfungsi di IE tanpa polyfill?
André Pena
2
@andrerpena caniuse.com/#search=addeventlistener menunjukkan ie8 akan memiliki masalah
nnnn
2
@nnnn. Saya melihat. Ya .. Jadi solusi saya tidak bekerja pada IE 8, tetapi bekerja dari 9 pada :). Terima kasih.
André Pena
35
Adakah yang benar-benar masih peduli dengan IE8? Atau hanya kebiasaan?
frostymarvelous
48

Solusi yang sangat sederhana:

resize = () => this.forceUpdate()

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

componentWillUnmount() {
  window.removeEventListener('resize', this.resize)
}
senornestor
sumber
12
Jangan lupa untuk membatasi pembaruan kekuatan atau itu akan terlihat sangat glitchy.
k2snowman69
4
Juga jangan lupa untuk menghapus pendengar componentWillUnmount()!
Jemar Jones
Ini adalah solusi terbaik jika dicekik. Maksud saya jika forceUpdatediterapkan secara kondisional
asmmahmud
1
Itu bukan forceUpdate (hal yang dipanggil) yang perlu dicekik, itu adalah peristiwa ukuran menembak yang perlu diperlambat (hal yang ditembakkan). Ubah ukuran acara secara teknis dapat dipanggil di setiap piksel saat Anda mengubah ukuran jendela dari besar ke kecil. Ketika pengguna melakukan ini dengan cepat, itu lebih banyak peristiwa daripada yang Anda pedulikan. Lebih buruk lagi, Anda mengikat utas UI ke utas Javascript yang berarti aplikasi Anda akan mulai mendapatkan perasaan yang sangat lambat ketika mencoba menangani masing-masing acara secara terpisah.
k2snowman69
1
Alih-alih menjalankan fungsi pengubahan ukuran pada setiap piksel, Anda menjalankannya dalam waktu singkat untuk memberikan ilusi fluiditas yang selalu Anda tangani pada acara pertama dan terakhir sehingga memberikan kesan bahwa itu dengan lancar menangani pengubahan ukuran.
k2snowman69
42

Ini adalah contoh sederhana dan singkat menggunakan es6 tanpa 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>
    );
  }
}

kait

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>
  );
};
suara
sumber
4
Ini adalah jawaban yang bagus dan ringkas tetapi AFAICT memiliki satu bug: kecuali saya salah, ::operator bind mengembalikan nilai baru setiap kali diterapkan. Jadi pendengar acara Anda tidak akan benar-benar tidak terdaftar, karena removeEventListenerakhirnya Anda melewati fungsi yang berbeda dari yang semula diteruskan addEventListener.
natevw
21

Pada React 16.8 Anda dapat menggunakan 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}</>
}
noetix
sumber
13

Sunting 2018 : sekarang Bereaksi memiliki dukungan kelas pertama untuk konteks


Saya akan mencoba memberikan jawaban umum, yang menargetkan masalah khusus ini tetapi juga masalah yang lebih umum.

Jika Anda tidak peduli dengan efek samping lib, Anda bisa menggunakan sesuatu seperti Packery

Jika Anda menggunakan Flux, Anda bisa membuat toko yang berisi properti jendela sehingga Anda menjaga fungsi render murni tanpa harus menanyakan objek jendela setiap kali.

Dalam kasus lain di mana Anda ingin membangun situs web responsif tetapi Anda lebih suka Bereaksi gaya inline ke kueri media, atau ingin perilaku HTML / JS berubah sesuai dengan lebar jendela, baca terus:

Apa konteks Bereaksi dan mengapa saya membicarakannya

Bereaksi konteks dan tidak dalam API publik dan mengizinkan untuk meneruskan properti ke seluruh hierarki komponen.

Konteks reaksi sangat berguna untuk diteruskan ke seluruh aplikasi Anda hal-hal yang tidak pernah berubah (digunakan oleh banyak kerangka kerja Flux melalui mixin). Anda dapat menggunakannya untuk menyimpan invarian bisnis aplikasi (seperti userId yang terhubung, sehingga tersedia di mana-mana).

Tetapi bisa juga digunakan untuk menyimpan hal-hal yang bisa berubah. Masalahnya adalah ketika konteksnya berubah, semua komponen yang menggunakannya harus di-render ulang dan tidak mudah untuk melakukannya, solusi terbaiknya adalah dengan meng-unmount / mengirim kembali seluruh aplikasi dengan konteks baru. Ingat forceUpdate tidak rekursif .

Jadi, seperti yang Anda pahami, konteksnya praktis, tetapi ada dampak kinerja saat berubah, jadi sebaiknya tidak terlalu sering berubah.

Apa yang dimasukkan ke dalam konteks

  • Invarian: seperti userId yang terhubung, sessionToken, apa pun ...
  • Hal-hal yang tidak sering berubah

Berikut adalah hal-hal yang tidak sering berubah:

Bahasa pengguna saat ini :

Itu tidak berubah sangat sering, dan ketika itu terjadi, ketika seluruh aplikasi diterjemahkan kita harus membuat ulang semuanya: usecase perubahan bahas panas yang sangat bagus

Properti jendela

Lebar dan tinggi agar tidak sering berubah tetapi ketika kita melakukan tata letak dan perilaku kita mungkin harus beradaptasi. Untuk tata letak terkadang mudah untuk menyesuaikan dengan permintaan media CSS, tetapi kadang-kadang tidak dan membutuhkan struktur HTML yang berbeda. Untuk perilaku Anda harus menangani ini dengan Javascript.

Anda tidak ingin merender ulang semuanya di setiap acara pengubahan ukuran, jadi Anda harus membatalkan aktivitas pengubahan ukuran.

Yang saya mengerti tentang masalah Anda adalah bahwa Anda ingin tahu berapa banyak item yang akan ditampilkan sesuai dengan lebar layar. Jadi, Anda harus mendefinisikan breakpoints responsif terlebih dahulu , dan menyebutkan jumlah jenis tata letak yang berbeda yang dapat Anda miliki.

Sebagai contoh:

  • Tata letak "1col", untuk lebar <= 600
  • Tata letak "2col", untuk 600 <lebar <1000
  • Tata letak "3col", untuk 1000 <= lebar

Pada mengubah ukuran acara (didebitkan), Anda dapat dengan mudah mendapatkan jenis tata letak saat ini dengan menanyakan objek jendela.

Kemudian Anda dapat membandingkan jenis tata letak dengan jenis tata letak sebelumnya, dan jika telah berubah, render ulang aplikasi dengan konteks baru: ini memungkinkan untuk menghindari rendering ulang aplikasi sama sekali ketika pengguna telah memicu mengubah ukuran acara tetapi sebenarnya tipe tata letak tidak berubah, jadi Anda hanya merender ulang saat diperlukan.

Setelah memilikinya, Anda cukup menggunakan jenis tata letak di dalam aplikasi Anda (dapat diakses melalui konteks) sehingga Anda dapat menyesuaikan kelas HTML, perilaku, kelas CSS ... Anda tahu jenis tata letak di dalam fungsi React render sehingga ini berarti Anda dapat dengan aman menulis situs web responsif dengan menggunakan gaya sebaris, dan tidak memerlukan mediaqueries sama sekali.

Jika Anda menggunakan Flux, Anda dapat menggunakan toko alih-alih konteks Bereaksi, tetapi jika aplikasi Anda memiliki banyak komponen responsif, mungkin lebih mudah menggunakan konteks?

Sebastien Lorber
sumber
10

Saya menggunakan solusi @senornestor, tetapi untuk sepenuhnya benar Anda harus menghapus pendengar acara juga:

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

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

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

Kalau tidak, Anda akan mendapatkan peringatan:

Peringatan: forceUpdate (...): Hanya dapat memperbarui komponen yang dipasang atau dipasang. Ini biasanya berarti Anda memanggil forceUpdate () pada komponen yang tidak di-mount. Ini adalah no-op. Silakan periksa kode untuk komponen XXX.

gkri
sumber
1
Anda mendapat peringatan karena suatu alasan: apa yang Anda lakukan adalah praktik yang buruk. Fungsi render () Anda harus merupakan fungsi murni dari properti dan status. Dalam kasus Anda, Anda harus menyimpan ukuran baru di negara bagian.
zoran404
7

Saya akan melewati semua jawaban di atas dan mulai menggunakan react-dimensions Komponen Orde Tinggi.

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

Cukup tambahkan importpanggilan fungsi dan sederhana , dan Anda dapat mengakses this.props.containerWidthdan this.props.containerHeightdi komponen Anda.

// Example using ES6 syntax
import React from 'react'
import Dimensions from 'react-dimensions'

class MyComponent extends React.Component {
  render() (
    <div
      containerWidth={this.props.containerWidth}
      containerHeight={this.props.containerHeight}
    >
    </div>
  )
}

export default Dimensions()(MyComponent) // Enhanced component
MindJuice
sumber
1
Ini tidak memberikan ukuran jendela, hanya wadahnya.
mjtamlyn
3
Ya, itulah intinya. Ukuran jendela sepele untuk ditemukan. Ukuran wadah jauh lebih sulit ditemukan dan jauh lebih berguna untuk komponen Bereaksi. Versi terbaru react-dimensionsbahkan berfungsi untuk dimensi yang diubah secara program (misalnya, ukuran div berubah, yang memengaruhi ukuran wadah Anda, jadi Anda perlu memperbarui ukuran Anda). Jawaban Ben Alpert hanya membantu di jendela browser mengubah ukuran acara. Lihat di sini: github.com/digidem/react-dimensions/issues/4
MindJuice
1
react-dimensionsmenangani perubahan dalam ukuran jendela, perubahan tata letak flexbox dan perubahan karena ukuran JavaScript. Saya pikir itu menutupinya. Apakah Anda memiliki contoh yang tidak ditangani dengan benar?
MindJuice
2
Ukuran jendela sepele untuk didapat. Tidak perlu perpustakaan untuk itu: window.innerWidth, window.innerHeight. react-dimensionsmemecahkan bagian yang lebih penting dari masalah, dan juga memicu kode tata letak Anda ketika jendela diubah ukurannya (serta ketika ukuran wadah berubah).
MindJuice
1
Proyek ini tidak lagi dipelihara secara aktif.
Parabolord
7

Kode ini menggunakan API konteks Bereaksi baru :

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

Maka Anda dapat menggunakannya di mana pun Anda inginkan dalam kode Anda seperti ini:

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

Anda tidak perlu memaksakan re-render.

Ini mungkin tidak membantu OP, tetapi dalam kasus saya, saya hanya perlu memperbarui widthdan heightatribut di kanvas saya (yang tidak dapat Anda lakukan dengan CSS).

Ini terlihat seperti ini:

import React from 'react';
import styled from 'styled-components';
import {throttle} from 'lodash';

class Canvas extends React.Component {

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

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

    resize = throttle(() => {
        this.canvas.width = this.canvas.parentNode.clientWidth;
        this.canvas.height = this.canvas.parentNode.clientHeight;
    },50)

    setRef = node => {
        this.canvas = node;
    }

    render() {
        return <canvas className={this.props.className} ref={this.setRef} />;
    }
}

export default styled(Canvas)`
   cursor: crosshair;
`
Mpen
sumber
5

Tidak yakin apakah ini pendekatan terbaik, tetapi yang berhasil bagi saya adalah pertama kali membuat Toko, saya menyebutnya 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;

Lalu saya menggunakan toko itu di komponen saya, agak seperti ini:

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, file-file ini sebagian telah dipangkas.

David Sinclair
sumber
1
Status store hanya boleh berubah sebagai respons terhadap tindakan yang dikirim. Ini jelas bukan cara yang baik untuk melakukan ini.
dingin
2
@ Frostymarvelous memang, mungkin lebih baik dipindahkan ke komponen seperti pada jawaban lainnya.
David Sinclair
@ DavidSinclair Atau lebih baik lagi, onresizebisa mengirim a WindowResizedAction.
John Weisz
1
Ini berlebihan.
Jason Rice
5

Saya tahu ini telah dijawab tetapi saya pikir saya akan membagikan solusi saya sebagai jawaban teratas, meskipun hebat, sekarang mungkin sedikit ketinggalan jaman.

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

Satu-satunya perbedaan adalah saya melakukan debouncing (hanya menjalankan setiap 200 ms) updateWindowDimensions pada event resize untuk sedikit meningkatkan kinerja, TETAPI jangan debouncing ketika dipanggil ComponentDidMount.

Saya menemukan debounce membuatnya agak lambat untuk dipasang kadang-kadang jika Anda memiliki situasi di mana ia sering dipasang.

Hanya sedikit pengoptimalan tetapi harap ini membantu seseorang!

Matt Wills
sumber
Di mana definisi initUpdateWindowDimensionsmetode?
Matt
Itu didefinisikan dalam konstruktor :) itu hanya updateWindowDimensions terikat ke komponen tetapi tanpa debounce.
Matt Wills
3

Hanya untuk meningkatkan solusi @ senornestor untuk digunakan forceUpdatedan solusi @ gkri untuk menghapus resizependengar acara pada komponen unmount:

  1. jangan lupa untuk membatasi (atau menolak) panggilan untuk mengubah ukuran
  2. pastikan bind(this)di konstruktor
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>
    )
  }
}

Metode lain adalah dengan menggunakan keadaan "dummy" alih-alih forceUpdate:

import React from 'react'
import { throttle } from 'lodash'

class Foo extends React.Component {
  constructor(props) {
    super(props)
    this.state = { foo: 1 }
    this.resize = throttle(this.resize.bind(this), 100)
  }

  resize = () => this.setState({ foo: 1 })

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

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

  render() {
    return (
      <div>{window.innerWidth} x {window.innerHeight}</div>
    )
  }
}
kimbaudi
sumber
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();
};

Hanya perlu mendefinisikan fungsi ukuran acara.

Kemudian perbarui ukuran penyaji (kanvas), tetapkan rasio aspek baru untuk kamera.

Lepas dan remouting adalah solusi gila menurut saya ....

di bawah ini adalah mount jika diperlukan.

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

Ingin membagikan hal keren yang baru saja saya temukan menggunakan ini 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 akan mengembalikan benar atau salah jika jendela lebih tinggi atau lebih rendah dari nilai lebar-max yang ditentukan, ini berarti tidak perlu mencekik pendengar, karena matchMedia hanya menyala sekali ketika boolean berubah.

Kode saya dapat dengan mudah disesuaikan untuk dimasukkan useStateuntuk menyimpan pengembalian boolean matchMedia, dan menggunakannya untuk membuat komponen, tindakan kebakaran, dll.

Riley Brown
sumber
1

Harus mengikatnya ke 'ini' di konstruktor untuk membuatnya bekerja dengan sintaks Kelas

class MyComponent extends React.Component {
  constructor(props) {
    super(props)
    this.resize = this.resize.bind(this)      
  }
  componentDidMount() {
    window.addEventListener('resize', this.resize)
  }
  componentWillUnmount() {
    window.removeEventListener('resize', this.resize)
  }
}
Jim Perris
sumber
1

Terima kasih atas jawabannya. Inilah React + Recompose saya . Ini adalah Fungsi Orde Tinggi yang mencakup windowHeightdan windowWidthproperti ke komponen.

const withDimensions = compose(
 withStateHandlers(
 ({
   windowHeight,
   windowWidth
 }) => ({
   windowHeight: window.innerHeight,
   windowWidth: window.innerWidth
 }), {
  handleResize: () => () => ({
    windowHeight: window.innerHeight,
    windowWidth: window.innerWidth
  })
 }),
 lifecycle({
   componentDidMount() {
   window.addEventListener('resize', this.props.handleResize);
 },
 componentWillUnmount() {
  window.removeEventListener('resize');
 }})
)
dumorango
sumber
1

https://github.com/renatorib/react-sizes adalah HOC untuk melakukan ini sambil tetap mempertahankan kinerja yang baik.

import React from 'react'
import withSizes from 'react-sizes'

@withSizes(({ width }) => ({ isMobile: width < 480 }))
class MyComponent extends Component {
  render() {
    return <div>{this.props.isMobile ? 'Is Mobile' : 'Is Not Mobile'}</div>
  }
}

export default MyComponent
Russell Cohen
sumber
0

Untuk alasan ini lebih baik adalah jika Anda menggunakan data ini dari data file CSS atau JSON, dan kemudian dengan menetapkan data ini status baru dengan this.state ({width: "some value", height: "some value"}); atau menulis kode yang menggunakan data data layar lebar dalam pekerjaan sendiri jika Anda ingin menampilkan gambar yang responsif

Miodrag Trajanovic
sumber