Chế độ xem lại trên trình duyệt thay đổi kích thước với React


313

Làm cách nào tôi có thể khiến React hiển thị lại chế độ xem khi cửa sổ trình duyệt được thay đổi kích thước?

Lý lịch

Tôi có một số khối mà tôi muốn bố trí riêng trên trang, tuy nhiên tôi cũng muốn chúng cập nhật khi cửa sổ trình duyệt thay đổi. Kết quả cuối cùng sẽ giống như bố cục Pinterest của Ben Holland , nhưng được viết bằng React không chỉ jQuery. Tôi vẫn còn một lối thoát.

Đây là ứng dụng của tôi:

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

Sau đó, tôi có Blockthành phần (tương đương với một Pintrong ví dụ Pinterest ở trên):

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

và danh sách / bộ sưu tập của 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>
    );
  }
});

Câu hỏi

Tôi có nên thêm kích thước cửa sổ của jQuery không? Nếu vậy thì ở đâu?

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

Có cách nào khác Reac React làm điều này không?

Câu trả lời:


531

Sử dụng móc phản ứng:

Bạn có thể xác định một Hook tùy chỉnh lắng nghe resizesự kiện cửa sổ , đại loại như thế này:

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

Ưu điểm ở đây là logic được gói gọn và bạn có thể sử dụng Hook này ở bất cứ đâu bạn muốn sử dụng kích thước cửa sổ.

Sử dụng các lớp React:

Bạn có thể nghe trong componentDidMount, một cái gì đó giống như thành phần này chỉ hiển thị kích thước cửa sổ (như <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
Cái này hoạt động ra sao? Truyền this.updateDimensionsđến addEventListenerchỉ là một tham chiếu hàm trần sẽ không có giá trị thiskhi được gọi. Nên sử dụng một hàm ẩn danh hoặc một lệnh gọi .bind () để thêm thishoặc tôi đã hiểu nhầm?
fadedbee

24
@chrisdew Tôi đến hơi muộn ở đây, nhưng React tự động liên kết thischo bất kỳ phương thức nào được xác định trực tiếp trên thành phần.
Michelle Tilley

3
@MattDell vâng, các lớp ES6 chỉ là các lớp bình thường, vì vậy không có tự động ràng buộc với chúng.
Michelle Tilley

30
Không cần jQuery - sử dụng innerHeightinnerWidthtừ window. Và bạn có thể bỏ qua componentWillMountnếu bạn sử dụng getInitialStateđể thiết lập heightwidth.
thở dài

2
@MattDell vẻ như ::cú pháp ràng buộc là ra bây giờ sitepoint.com/bind-javascripts-this-keyword-react "các nhà điều hành bind (: :) sẽ không là một phần của ES7, như ES7 tính năng thiết lập đã bị đóng băng trong tháng hai , toán tử liên kết là một đề xuất cho ES8 "
Jarrod Smith

130

@SophieAlpert đã đúng, +1, tôi chỉ muốn cung cấp một phiên bản sửa đổi của giải pháp của cô ấy, không có jQuery , dựa trên câu trả lời này .

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

chỉ hoạt động khi bạn cài đặt các polyfill liên quan đến IE của mình cho người nghe sự kiện JS vanilla
nnnn

@nnnn bạn có thể giải thích? Tôi thừa nhận tôi chỉ thử nghiệm điều này trong Chrome. Bạn đang nói window.addEventListener sẽ không hoạt động trên IE mà không có polyfill?
André Pena

2
@andrerpena caniuse.com/#search=addeventlistener chỉ ra tức là8 sẽ có vấn đề
nnnn

2
@nnnn. Tôi hiểu rồi. Có .. Vì vậy, giải pháp của tôi không hoạt động trên IE 8, nhưng hoạt động từ 9 trở đi :). Cảm ơn.
André Pena

35
Có ai thực sự vẫn quan tâm đến IE8? Hay đó chỉ là thói quen?
băng giá

48

Một giải pháp rất đơn giản:

resize = () => this.forceUpdate()

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

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

12
Đừng quên tăng tốc cập nhật lực lượng hoặc nó sẽ trông rất rối mắt.
k2snowman69

4
Cũng đừng quên loại bỏ người nghe trên componentWillUnmount()!
Jemar Jones

Đây là giải pháp tốt nhất nếu nó được điều chỉnh. Tôi có nghĩa là nếu forceUpdateđược áp dụng có điều kiện
asmmahmud

1
Đó không phải là ForceUpdate (thứ được gọi) cần được điều chỉnh, đó là sự kiện thay đổi kích thước cần phải được điều chỉnh (điều bắn). Thay đổi kích thước các sự kiện về mặt kỹ thuật có thể được gọi ở mọi pixel khi bạn thay đổi kích thước cửa sổ từ lớn sang nhỏ. Khi người dùng thực hiện việc này nhanh chóng, đó là nhiều sự kiện hơn bạn quan tâm. Tệ hơn nữa, bạn đang ràng buộc luồng UI với luồng Javascript, nghĩa là ứng dụng của bạn sẽ bắt đầu có cảm giác chậm nghiêm trọng khi nó cố gắng xử lý từng sự kiện riêng lẻ.
k2snowman69

1
Thay vì chạy chức năng thay đổi kích thước ở mọi pixel, bạn chạy nó trong một khoảng thời gian ngắn định kỳ để tạo ảo giác về tính trôi chảy mà bạn luôn xử lý sự kiện đầu tiên và cuối cùng do đó mang lại cảm giác rằng nó xử lý thay đổi kích thước một cách trôi chảy.
k2snowman69

42

Đây là một ví dụ đơn giản và ngắn gọn về việc sử dụng es6 mà không cần 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>
    );
  }
}

móc

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
Đây là một câu trả lời ngắn gọn, súc tích nhưng AFAICT có một lỗi: trừ khi tôi nhầm, ::toán tử liên kết trả về một giá trị mới mỗi lần áp dụng. Vì vậy, trình lắng nghe sự kiện của bạn sẽ không thực sự được đăng ký, vì removeEventListenerkết thúc của bạn chuyển qua một chức năng khác với chức năng ban đầu được truyền vào addEventListener.
natevw

21

Kể từ React 16.8, bạn có thể sử dụng Hook !

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

Chỉnh sửa 2018 : bây giờ React có hỗ trợ lớp đầu tiên cho ngữ cảnh


Tôi sẽ cố gắng đưa ra một câu trả lời chung chung, nhắm vào vấn đề cụ thể này nhưng cũng có một vấn đề chung hơn.

Nếu bạn không quan tâm đến libs tác dụng phụ, bạn chỉ cần sử dụng một cái gì đó như Packery

Nếu bạn sử dụng Flux, bạn có thể tạo một cửa hàng chứa các thuộc tính cửa sổ để bạn giữ chức năng kết xuất thuần túy mà không phải truy vấn đối tượng cửa sổ mọi lúc.

Trong các trường hợp khác khi bạn muốn xây dựng một trang web phản hồi nhưng bạn thích React kiểu nội tuyến hơn cho các truy vấn phương tiện hoặc muốn hành vi HTML / JS thay đổi theo chiều rộng cửa sổ, hãy tiếp tục đọc:

Bối cảnh React là gì và tại sao tôi nói về nó

Phản ứng bối cảnh an không có trong API công khai và cho phép chuyển các thuộc tính cho toàn bộ phân cấp các thành phần.

Phản ứng bối cảnh đặc biệt hữu ích để chuyển đến toàn bộ ứng dụng của bạn những thứ không bao giờ thay đổi (nó được sử dụng bởi nhiều khung Flux thông qua một mixin). Bạn có thể sử dụng nó để lưu trữ các bất biến kinh doanh ứng dụng (như userId được kết nối, để nó có sẵn ở mọi nơi).

Nhưng nó cũng có thể được sử dụng để lưu trữ những thứ có thể thay đổi. Vấn đề là khi bối cảnh thay đổi, tất cả các thành phần sử dụng nó sẽ được kết xuất lại và không dễ thực hiện, giải pháp tốt nhất thường là ngắt kết nối / kết nối lại toàn bộ ứng dụng với bối cảnh mới. Hãy nhớ ForceUpdate không được đệ quy .

Vì vậy, như bạn hiểu, bối cảnh là thực tế, nhưng có tác động hiệu suất khi nó thay đổi, vì vậy nó không nên thay đổi quá thường xuyên.

Đặt gì trong bối cảnh

  • Bất biến: như userId được kết nối, sessionToken, bất cứ điều gì ...
  • Những thứ không thay đổi thường xuyên

Đây là những thứ không thay đổi thường xuyên:

Ngôn ngữ người dùng hiện tại :

Nó không thay đổi thường xuyên, và khi có, vì toàn bộ ứng dụng được dịch, chúng tôi phải kết xuất lại mọi thứ: một usecase rất hay về thay đổi ngôn ngữ nóng

Các thuộc tính cửa sổ

Chiều rộng và chiều cao để không thay đổi thường xuyên nhưng khi chúng ta thực hiện bố cục và hành vi của chúng ta có thể phải thích nghi. Đối với bố cục đôi khi thật dễ dàng để tùy chỉnh với các phương tiện truyền thông CSS, nhưng đôi khi không phải vậy và đòi hỏi một cấu trúc HTML khác. Đối với hành vi bạn phải xử lý việc này với Javascript.

Bạn không muốn kết xuất lại mọi thứ trên mỗi sự kiện thay đổi kích thước, vì vậy bạn phải gỡ bỏ các sự kiện thay đổi kích thước.

Điều tôi hiểu về vấn đề của bạn là bạn muốn biết có bao nhiêu mục để hiển thị theo chiều rộng màn hình. Vì vậy, trước tiên bạn phải xác định điểm dừng đáp ứng và liệt kê số loại bố cục khác nhau mà bạn có thể có.

Ví dụ:

  • Bố cục "1col", cho chiều rộng <= 600
  • Bố cục "2col", cho 600 <chiều rộng <1000
  • Bố cục "3col", cho 1000 <= chiều rộng

Khi thay đổi kích thước các sự kiện (được công bố), bạn có thể dễ dàng có được kiểu bố trí hiện tại bằng cách truy vấn đối tượng cửa sổ.

Sau đó, bạn có thể so sánh loại bố cục với loại bố cục cũ và nếu nó đã thay đổi, hãy kết xuất lại ứng dụng với bối cảnh mới: điều này cho phép tránh hiển thị lại ứng dụng khi người dùng đã kích hoạt thay đổi kích thước sự kiện nhưng thực tế là kiểu bố cục không thay đổi, vì vậy bạn chỉ kết xuất lại khi có yêu cầu.

Khi bạn đã có điều đó, bạn chỉ cần sử dụng loại bố cục bên trong ứng dụng của mình (có thể truy cập qua ngữ cảnh) để bạn có thể tùy chỉnh HTML, hành vi, lớp CSS ... Bạn biết loại bố cục của mình bên trong chức năng kết xuất React nên điều này có nghĩa là bạn có thể viết các trang web đáp ứng một cách an toàn bằng cách sử dụng các kiểu nội tuyến và hoàn toàn không cần phương tiện truyền thông.

Nếu bạn sử dụng Flux, bạn có thể sử dụng một cửa hàng thay vì bối cảnh React, nhưng nếu ứng dụng của bạn có nhiều thành phần đáp ứng thì có thể sử dụng ngữ cảnh đơn giản hơn?


10

Tôi sử dụng giải pháp của @senornestor, nhưng để hoàn toàn chính xác, bạn cũng phải loại bỏ trình lắng nghe sự kiện:

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

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

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

Nếu không, bạn sẽ nhận được cảnh báo:

Cảnh báo: forceUpdate (...): Chỉ có thể cập nhật một thành phần được gắn hoặc gắn. Điều này thường có nghĩa là bạn được gọi là ForceUpdate () trên một thành phần chưa từng có. Đây là một không-op. Vui lòng kiểm tra mã cho thành phần XXX.


1
Bạn đang nhận được cảnh báo vì một lý do: những gì bạn đang làm là thực hành tồi. Hàm render () của bạn phải là một hàm thuần túy của đạo cụ và trạng thái. Trong trường hợp của bạn, bạn nên lưu kích thước mới trong trạng thái.
zoran404

7

Tôi sẽ bỏ qua tất cả các câu trả lời ở trên và bắt đầu sử dụng react-dimensionsThành phần bậc cao.

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

Chỉ cần thêm một importcuộc gọi đơn giản và chức năng, và bạn có thể truy cập this.props.containerWidththis.props.containerHeighttrong thành phần của bạn.

// 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
Điều này không cho kích thước của cửa sổ, chỉ là container.
mjtamlyn

3
Vâng, đó là toàn bộ vấn đề. Kích thước của cửa sổ là tầm thường để tìm. Kích thước của một container khó tìm hơn và hữu ích hơn nhiều cho thành phần React. Phiên bản mới nhất react-dimensionsthậm chí hoạt động cho các kích thước được thay đổi theo chương trình (ví dụ: kích thước của div đã thay đổi, ảnh hưởng đến kích thước của vùng chứa của bạn, vì vậy bạn cần cập nhật kích thước của mình). Câu trả lời của Ben Alpert chỉ giúp trên cửa sổ trình duyệt thay đổi kích thước các sự kiện. Xem tại đây: github.com/digidem/react-dimensions/issues/4
MindJuice

1
react-dimensionsxử lý các thay đổi về kích thước của cửa sổ, thay đổi bố cục flexbox và thay đổi do thay đổi kích thước JavaScript. Tôi nghĩ rằng nó bao gồm nó. Bạn có một ví dụ rằng nó không xử lý đúng?
MindJuice

2
Kích thước cửa sổ là tầm thường để có được. Không cần một thư viện cho điều đó: window.innerWidth, window.innerHeight. react-dimensionsgiải quyết phần quan trọng hơn của vấn đề và cũng kích hoạt mã bố cục của bạn khi cửa sổ được thay đổi kích thước (cũng như khi kích thước vùng chứa thay đổi).
MindJuice

1
Dự án này không còn được duy trì tích cực.
Parabolord

7

Mã này đang sử dụng API ngữ cảnh React mới :

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

Sau đó, bạn có thể sử dụng nó bất cứ nơi nào bạn muốn trong mã của bạn như thế này:

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

6

Bạn không nhất thiết phải buộc kết xuất lại.

Điều này có thể không giúp OP, nhưng trong trường hợp của tôi, tôi chỉ cần cập nhật các thuộc tính widthheightthuộc tính trên khung vẽ của mình (điều mà bạn không thể làm với CSS).

Nó trông như thế này:

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

Không chắc đây có phải là cách tiếp cận tốt nhất không, nhưng điều đầu tiên đối với tôi là lần đầu tiên tạo Cửa hàng, tôi đã gọi nó là 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;

Sau đó, tôi đã sử dụng cửa hàng đó trong các thành phần của mình, đại loại như thế này:

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, những tập tin này đã được cắt bớt một phần.


1
Trạng thái lưu trữ chỉ nên thay đổi để đáp ứng với một hành động được gửi đi. Đây chắc chắn không phải là một cách tốt để làm điều này.
băng giá

2
@frostymarvelous thực sự, có lẽ tốt hơn di chuyển vào thành phần như trong các câu trả lời khác.
David Sinclair

@DavidSinclair Hoặc thậm chí tốt hơn, onresizecó thể gửi a WindowResizedAction.
John Weisz

1
Đây là quá mức cần thiết.
Jason Rice

5

Tôi biết điều này đã được trả lời nhưng chỉ nghĩ rằng tôi chia sẻ giải pháp của mình là câu trả lời hàng đầu, mặc dù tuyệt vời, giờ đây có thể hơi lỗi thời.

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

Sự khác biệt duy nhất thực sự là tôi đang gỡ lỗi (chỉ chạy mỗi 200ms) bản cập nhậtWindowDimensions về sự kiện thay đổi kích thước để tăng hiệu suất một chút, NHƯNG không gỡ lỗi khi được gọi trên ElementDidMount.

Tôi đã tìm thấy bản sửa lỗi khiến đôi khi nó khá chậm để gắn kết nếu bạn gặp tình huống thường xuyên gắn kết.

Chỉ là một tối ưu hóa nhỏ nhưng hy vọng nó sẽ giúp được ai đó!


Định nghĩa của initUpdateWindowDimensionsphương pháp ở đâu?
Matt

Nó được định nghĩa trong hàm tạo :) nó chỉ là updateWindowDimensions bị ràng buộc với thành phần nhưng không có phần gỡ lỗi.
Matt sẽ làm việc vào

3

Chỉ cần cải thiện giải pháp của @ senornestor để sử dụng forceUpdatevà giải pháp của @ gkri để loại bỏ trình resizelắng nghe sự kiện trên thành phần unmount:

  1. đừng quên điều tiết (hoặc gỡ bỏ) cuộc gọi để thay đổi kích thước
  2. đảm bảo bind(this)trong hàm tạo
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>
    )
  }
}

Một phương pháp khác là chỉ sử dụng trạng thái "giả" thay vì 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();
};

Chỉ cần xác định thay đổi kích thước chức năng sự kiện.

Sau đó cập nhật kích thước trình kết xuất (canvas), gán tỷ lệ khung hình mới cho máy ảnh.

Unmounting và remout là một giải pháp điên rồ theo ý kiến ​​của tôi ....

dưới đây là gắn kết nếu cần thiết.

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

2

Muốn chia sẻ điều tuyệt vời này, tôi vừa tìm thấy bằng cách sử dụng 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 sẽ trả về true hoặc false nếu cửa sổ cao hơn hoặc thấp hơn giá trị độ rộng tối đa đã chỉ định, điều này có nghĩa là không cần điều tiết người nghe, vì matchMedia chỉ kích hoạt một lần khi boolean thay đổi.

Mã của tôi có thể dễ dàng được điều chỉnh để bao gồm useStateđể lưu trả về boolean matchMedia và sử dụng nó để hiển thị một cách có điều kiện một thành phần, hành động cháy, v.v.


1

Phải liên kết nó với 'this' trong hàm tạo để làm cho nó hoạt động với cú pháp Class

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

1

Cảm ơn tất cả mọi người vì đã trả lời. Đây là React + Recompose của tôi . Đó là Hàm bậc cao bao gồm các thuộc tính windowHeightwindowWidththuộc tính cho thành phần.

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 là một HOC để làm điều này trong khi vẫn duy trì hiệu suất tốt.

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

Vì lý do này tốt hơn là nếu bạn sử dụng dữ liệu này từ dữ liệu tệp CSS hoặc JSON, và sau đó với dữ liệu này sẽ đặt trạng thái mới với this.state ({width: "some value", height: "some value"}); hoặc viết mã người sử dụng dữ liệu của dữ liệu màn hình chiều rộng để tự làm việc nếu bạn muốn hiển thị hình ảnh phản hồi

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.