React를 사용하여 브라우저 크기 조정의 렌더링 뷰 브라우저 창이 변경되면

브라우저 창의 크기를 조정할 때 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
});

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



답변

반응 고리 사용 :

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


답변

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


답변

매우 간단한 해결책 :

resize = () => this.forceUpdate()

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

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


답변

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


답변

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


답변

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


답변

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

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

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

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

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

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