React를 사용하여 브라우저 크기 조정의 렌더링 뷰


313

브라우저 창의 크기를 조정할 때 React에서 뷰를 다시 렌더링하려면 어떻게해야합니까?

배경

페이지에서 개별적으로 레이아웃하려는 블록이 있지만 브라우저 창이 변경되면 업데이트하기를 원합니다. 최종 결과는 Ben Holland의 Pinterest 레이아웃과 비슷하지만 jQuery뿐만 아니라 React를 사용하여 작성됩니다. 나는 아직도 방법입니다.

암호

내 응용 프로그램은 다음과 같습니다.

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

그런 다음 Block구성 요소가 있습니다 ( Pin위 Pinterest 예의 a와 동일 ).

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

및 목록 / 컬렉션 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>
    );
  }
});

질문

jQuery의 창 크기를 추가해야합니까? 그렇다면 어디서?

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

이 작업을 수행하는 더 "반응적인"방법이 있습니까?

답변:


531

반응 고리 사용 :

resize다음과 같이 window 이벤트 를 수신하는 사용자 정의 후크를 정의 할 수 있습니다 .

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

여기서 장점은 논리가 캡슐화되어 있으며 창 크기를 사용하려는 모든 위치에서이 후크를 사용할 수 있다는 것입니다.

React 클래스 사용하기 :

componentDidMount에서 창 크기를 표시하는 다음과 같은 구성 요소를들을 수 있습니다 (예 <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
어떻게 작동합니까? this.updateDimensions에 통과 addEventListener에 대한 값이 없습니다 단지 베어 함수 참조가 this전화했을 때를. 익명 함수 또는 .bind () 호출을 사용하여 추가 this해야합니까, 아니면 오해해야합니까?
fadedbee

24
@ chrisdew 여기 조금 늦었지만 this구성 요소에 직접 정의 된 모든 메소드 에 대해 자동 바인딩 을 수행하십시오.
Michelle Tilley 2016 년

3
@MattDell 예, ES6 클래스는 일반 클래스이므로 자동 바인딩이 없습니다.
Michelle Tilley

30
아니 jQuery를 필요 - 사용 innerHeight하고 innerWidth부터 window. 그리고 당신은 건너 뛸 수 있습니다 componentWillMount당신이 사용하는 경우 getInitialState설정 height하고 width.
sighrobot

2
@MattDell은 현재 ::바인드 구문이 사용 되지 않는 것 같습니다. sitepoint.com/bind-javascripts-this-keyword-react "2 월에 ES7 기능 세트가 정지 되었으므로 바인드 연산자 (: :)는 ES7의 일부가 아닙니다 , 바인드 연산자는 ES8에 대한 제안입니다 "
Jarrod Smith

130

@SophieAlpert는 옳습니다. +1, 나는 이 답변을 기반으로 jQuery없이 수정 된 버전의 솔루션을 제공하고 싶습니다 .

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

바닐라 JS 이벤트 리스너를 위해 IE 관련 폴리 필을 설정 한 경우에만 작동
nnnn

@nnnn 당신은 정교한 수 있습니까? Chrome에서만 테스트했음을 인정합니다. window.addEventListener가 polyfills가없는 IE에서 작동하지 않는다고 말하고 있습니까?
André Pena

2
@andrerpena caniuse.com/#search=addeventlistener 는 ie8에 문제가 있음을 나타냅니다
nnnn

2
@nnnn. 내가 참조. 예 .. 그래서 내 솔루션은 IE 8에서는 작동하지 않지만 9에서 작동합니다 :). 감사.
André Pena

35
아무도 여전히 IE8에 관심이 있습니까? 아니면 그냥 습관입니까?
frostymarvelous

48

매우 간단한 해결책 :

resize = () => this.forceUpdate()

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

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

12
강제 업데이트를 조절하는 것을 잊지 마십시오. 그렇지 않으면 매우 고장날 것입니다.
k2snowman69

4
또한 청취자를 제거하는 것을 잊지 마십시오 componentWillUnmount()!
Jemar Jones

이것이 조절되는 경우 가장 좋은 솔루션입니다. forceUpdate조건부로 적용되는 경우
asmmahmud

1
스로틀을해야하는 것은 forceUpdate (호출되는 것)가 아니라 스로틀해야하는 크기 조정 이벤트 발생입니다 (발사 물). 창의 크기를 크거나 작게 조정하면 모든 픽셀에서 크기 조정 이벤트를 기술적으로 호출 할 수 있습니다. 사용자가이 작업을 빠르게 수행하면 관심있는 것보다 더 많은 이벤트가 발생합니다. 더 나쁜 것은 UI 스레드를 Javascript 스레드에 바인딩한다는 것입니다. 즉, 각 이벤트를 개별적으로 처리하려고 할 때 앱이 심각하게 느리게 시작합니다.
k2snowman69

1
모든 픽셀에서 크기 조정 기능을 실행하는 대신 약간의주기적인 시간에 실행하여 항상 첫 번째 이벤트와 마지막 이벤트를 처리하는 유동성의 환상을 제공하므로 크기 조정이 유동적으로 처리되고 있다는 느낌을줍니다.
k2snowman69

42

jQuery없이 es6을 사용하는 간단하고 간단한 예입니다.

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

갈고리

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
이것은 간결하고 좋은 대답이지만 AFAICT에는 하나의 버그가 있습니다. 실수하지 않으면 ::바인드 연산자는 적용될 때마다 새 값을 반환합니다. 따라서 이벤트 리스너는 실제로 등록되지 않은 상태가되지 않습니다. removeEventListener결국 원래 전달 된 것과 다른 기능을 전달하기 때문 addEventListener입니다.
natevw

21

React 16.8부터 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

2018 편집 : React는 컨텍스트에 대한 일급 지원을 제공합니다.


나는이 특정 문제를 목표로하지만 더 일반적인 문제를 목표로 일반적인 대답을하려고합니다.

부작용 라이브러리에 신경 쓰지 않는다면 Packery 와 같은 것을 사용할 수 있습니다.

Flux를 사용하는 경우, 매번 윈도우 객체를 쿼리 할 필요없이 순수한 렌더 기능을 유지하도록 윈도우 속성을 포함하는 상점을 작성할 수 있습니다.

반응 형 웹 사이트를 만들고 싶지만 미디어 쿼리에 대해 인라인 스타일로 반응하기를 선호하거나 HTML / JS 동작을 창 너비에 따라 변경하려는 경우 계속 읽으십시오.

React 컨텍스트 란 무엇이며 왜 이야기해야합니까

반응 컨텍스트 an은 공용 API에 없으며 속성을 전체 구성 요소 계층에 전달할 수 있습니다.

React 컨텍스트는 변경되지 않는 전체 앱에 전달하는 데 특히 유용합니다 (mixin을 통해 많은 Flux 프레임 워크에서 사용됨). 연결된 비즈니스 ID와 같이 앱 비즈니스 변형을 저장하는 데 사용할 수 있으므로 어디에서나 사용할 수 있습니다.

그러나 변경 될 수있는 것을 저장하는 데 사용될 수도 있습니다. 문제는 컨텍스트가 변경 될 때 컨텍스트를 사용하는 모든 구성 요소를 다시 렌더링해야하며 그렇게하기 쉽지 않다는 것입니다. 최상의 솔루션은 종종 전체 컨텍스트를 새 컨텍스트로 마운트 해제 / 다시 마운트하는 것입니다. 기억 forceUpdate가 순환되지 않습니다 .

이해할 수 있듯이 컨텍스트는 실용적이지만 변경 될 때 성능에 영향을 미치므로 너무 자주 변경해서는 안됩니다.

상황에 맞게

  • 불변 : 연결된 userId, sessionToken 등 무엇이든 ...
  • 자주 바뀌지 않는 것

자주 변경되지 않는 사항은 다음과 같습니다.

현재 사용자 언어 :

자주 변경되지는 않으며, 전체 앱이 번역 될 때 모든 것을 다시 렌더링해야합니다.

창 속성

너비와 높이는 자주 변경되지 않지만 레이아웃을 수행 할 때 레이아웃과 동작이 조정되어야 할 수 있습니다. 레이아웃의 경우 CSS 미디어 쿼리를 사용하여 쉽게 사용자 정의 할 수 있지만 때로는 그렇지 않고 다른 HTML 구조가 필요합니다. 동작을 위해서는 Javascript로이를 처리해야합니다.

모든 크기 조정 이벤트에서 모든 것을 다시 렌더링하지 않으려면 크기 조정 이벤트를 디 바운싱해야합니다.

내가 당신의 문제를 이해하는 것은 화면 너비에 따라 표시 할 항목 수를 알고 싶다는 것입니다. 따라서 먼저 반응중단 점 을 정의하고 가질 수있는 다양한 레이아웃 유형을 열거해야합니다.

예를 들면 다음과 같습니다.

  • 너비 <= 600 인 경우 레이아웃 "1col"
  • 600 <너비 <1000에 대한 레이아웃 "2col"
  • 1000 <= 너비의 레이아웃 "3col"

크기 조정 이벤트 (발표 됨)에서 창 객체를 쿼리하여 현재 레이아웃 유형을 쉽게 얻을 수 있습니다.

그런 다음 레이아웃 유형을 이전 레이아웃 유형과 비교할 수 있으며 변경된 경우 새 컨텍스트로 앱을 다시 렌더링하십시오. 이렇게하면 사용자가 크기 조정 이벤트를 트리거하지만 실제로는 앱을 다시 렌더링하지 않아도됩니다. 레이아웃 유형이 변경되지 않았으므로 필요할 때만 다시 렌더링하십시오.

일단 HTML, 비헤이비어, CSS 클래스를 사용자 정의 할 수 있도록 앱 내에서 레이아웃 유형 (컨텍스트를 통해 액세스 가능)을 간단히 사용할 수 있습니다. React 렌더 함수 내부의 레이아웃 유형을 알고 있으므로 인라인 스타일을 사용하여 반응 형 웹 사이트를 안전하게 작성할 수 있으며 미디어 쿼리가 전혀 필요하지 않습니다.

Flux를 사용하는 경우 React 컨텍스트 대신 상점을 사용할 수 있지만 앱에 반응 형 구성 요소가 많은 경우 컨텍스트를 사용하는 것이 더 간단 할 수 있습니까?


10

@senornestor의 솔루션을 사용하지만 완전히 정확하려면 이벤트 리스너도 제거해야합니다.

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

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

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

그렇지 않으면 경고가 나타납니다.

경고 : forceUpdate (...) : 마운트 또는 마운트 구성 요소 만 업데이트 할 수 있습니다. 이것은 일반적으로 마운트 해제 된 구성 요소에서 forceUpdate ()를 호출했음을 의미합니다. 이것은 no-op입니다. XXX 구성 요소의 코드를 확인하십시오.


1
당신은 이유에 대한 경고를 받고 있습니다 : 당신이하고있는 일은 나쁜 습관입니다. render () 함수는 props와 state의 순수한 함수 여야합니다. 귀하의 경우 상태에서 새 크기를 저장해야합니다.
zoran404

7

위의 모든 답변을 건너 뛰고 react-dimensions상위 구성 요소 사용을 시작합니다 .

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

그냥 간단하게 추가 import하고 함수 호출을, 당신은에 액세스 할 수 있습니다 this.props.containerWidththis.props.containerHeight구성 요소입니다.

// 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
이것은 창의 크기가 아니라 컨테이너 만 제공합니다.
mjtamlyn 2016 년

3
예, 그게 요점입니다. 창의 크기는 찾기 쉽지 않습니다. 컨테이너의 크기는 찾기가 훨씬 어렵고 React 컴포넌트에 훨씬 유용합니다. 최신 버전은 react-dimensions프로그래밍 방식으로 변경된 치수 (예 : div 크기가 변경되어 컨테이너 크기에 영향을 미치므로 크기를 업데이트해야 함)에도 작동합니다. Ben Alpert의 답변은 브라우저 창 크기 조정 이벤트에서만 도움이됩니다. 여기를 참조하십시오 : github.com/digidem/react-dimensions/issues/4
MindJuice

1
react-dimensions윈도우 크기 변경, flexbox 레이아웃 변경 및 JavaScript 크기 조정으로 인한 변경을 처리합니다. 나는 그것을 다룬다 고 생각한다. 제대로 처리되지 않는 예가 있습니까?
MindJuice

2
창 크기는 사소한 것입니다. 이를 위해 라이브러리가 필요하지 않습니다 : window.innerWidth, window.innerHeight. react-dimensions문제의 더 중요한 부분을 해결하고 창 크기를 조정할 때 (컨테이너 크기가 변경 될 때) 레이아웃 코드를 트리거합니다.
MindJuice

1
이 프로젝트는 더 이상 적극적으로 유지 관리되지 않습니다.
Parabolord

7

이 코드는 새로운 React 컨텍스트 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() {
      return <Provider value={this.state}>{this.props.children}</Provider>;
    }
  }

그런 다음 코드에서 원하는 위치에 다음과 같이 사용할 수 있습니다.

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

6

반드시 다시 렌더링 할 필요는 없습니다.

이것은 OP에 도움이되지 않을 수도 있지만 내 경우에는 캔버스 에서 widthheight속성 을 업데이트하기 만하면됩니다 (CSS로는 할 수 없음).

다음과 같이 보입니다 :

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

이것이 최선의 접근 방법인지 확실하지 않지만 저에게 효과적 인 것은 처음 상점을 만드는 것이 었습니다.

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;

그런 다음 구성 요소에서 해당 저장소를 다음과 같이 사용했습니다.

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

참고로,이 파일들은 부분적으로 잘 렸습니다.


1
전달 된 조치에 대한 응답으로 만 상점 상태가 변경되어야합니다. 이것은 확실히 좋은 방법이 아닙니다.
frostymarvelous

2
@frostymarvelous는 아마도 다른 답변에서와 같이 구성 요소로 더 잘 옮겨졌습니다.
David Sinclair

@DavidSinclair 또는 더 좋게는을 전달할 onresizeWindowResizedAction있습니다.
John Weisz

1
이것은 과잉입니다.
Jason Rice

5

나는 이것이 대답되었다는 것을 알고 있지만, 위대한 대답이지만 지금은 조금 구식 일 수 있기 때문에 내 솔루션을 공유 할 것이라고 생각했습니다.

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

유일한 차이점은 resize 이벤트에서 updateWindowDimensions를 제거하여 (200ms마다 실행) 성능을 약간 높이지만 ComponentDidMount에서 호출 될 때 해제하지 않는다는 것입니다.

나는 당신이 자주 마운트하는 상황이있는 경우 때로는 디 바운스가 마운트하기가 매우 게으른 것을 발견했습니다.

사소한 최적화이지만 누군가에게 도움이되기를 바랍니다!


initUpdateWindowDimensions방법의 정의는 어디에 있습니까 ?
Matt

생성자에 정의되어 있습니다 :) 단지 구성 요소에 바인딩되어 있지만 디 바운스가없는 updateWindowDimensions입니다.
Matt Wills

3

@senornestor의 솔루션을 개선하고 @gkri의 솔루션을 개선 하여 구성 요소 마운트 해제시 이벤트 리스너 forceUpdate를 제거하려면 resize다음을 수행하십시오.

  1. 크기 조정 요청을 조절 (또는 디 바운스)하는 것을 잊지 마십시오
  2. bind(this)생성자에서 확인하십시오
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>
    )
  }
}

또 다른 방법은 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();
};

크기 조정 이벤트 기능 만 정의하면됩니다.

그런 다음 렌더러 크기 (canvas)를 업데이트하고 카메라에 새로운 종횡비를 할당하십시오.

언 마운트와 remouting은 제 생각에 미친 해결책입니다 ...

아래는 필요한 경우 마운트입니다.

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

2

방금 찾은이 멋진 것을 공유하고 싶었습니다. 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 창이 지정된 최대 너비 값보다 높거나 낮은 경우 true 또는 false를 반환합니다. 이는 부울이 변경 될 때 matchMedia가 한 번만 실행되므로 리스너를 조절할 필요가 없음을 의미합니다.

useState부울 matchMedia 반환을 저장 하도록 포함 하도록 코드를 쉽게 조정 하고이를 사용하여 조건부로 구성 요소를 렌더링하고 화재 동작 등을 수행 할 수 있습니다.


1

클래스 구문으로 작업하기 위해 생성자에서 'this'에 바인딩해야했습니다.

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

답변 주셔서 감사합니다. 여기 내 React + Recompose 입니다. 구성 요소에 대한 windowHeightwindowWidth속성을 포함하는 고차 함수입니다 .

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 는 여전히 좋은 성능을 유지하면서이를 수행하는 HOC입니다.

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

이러한 이유로 CSS 또는 JSON 파일 데이터에서이 데이터를 사용하고 this.state ({width : "some value", height : "some value"}); 반응 형 쇼 이미지를 원할 경우 자체 작업에서 너비 화면 데이터의 데이터를 사용하는 코드 작성

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.