[javascript] “렌더링 후”코드에 반응합니까?

요소의 높이 ( “app-content”라고 함)를 동적으로 설정해야하는 앱이 있습니다. 앱의 “크롬”높이를 빼고 빼서 “응용 프로그램 콘텐츠”높이를 해당 제한 내에서 100 %에 맞게 설정합니다. 바닐라 JS, jQuery 또는 Backbone 뷰에서는 매우 간단하지만 React에서 올바른 프로세스가 무엇인지 알아 내려고 고심하고 있습니다.

아래는 예제 구성 요소입니다. app-content높이를 창의 100 %에서 ActionBarand 의 크기를 뺀 값 으로 설정할 수 있기를 원 BalanceBar하지만 모든 것이 렌더링되는 시점 과이 React 클래스에서 계산 항목을 어디에 넣을지 어떻게 알 수 있습니까?

/** @jsx React.DOM */
var List = require('../list');
var ActionBar = require('../action-bar');
var BalanceBar = require('../balance-bar');
var Sidebar = require('../sidebar');
var AppBase = React.createClass({
  render: function () {
    return (
      <div className="wrapper">
        <Sidebar />
        <div className="inner-wrapper">
          <ActionBar title="Title Here" />
          <BalanceBar balance={balance} />
          <div className="app-content">
            <List items={items} />
          </div>
        </div>
      </div>
    );
  }
});

module.exports = AppBase;



답변

componentDidMount ()

이 메소드는 컴포넌트가 렌더링 된 후에 한 번 호출됩니다. 따라서 코드는 다음과 같습니다.

var AppBase = React.createClass({
  componentDidMount: function() {
    var $this = $(ReactDOM.findDOMNode(this));
    // set el height and width etc.
  },

  render: function () {
    return (
      <div className="wrapper">
        <Sidebar />
          <div className="inner-wrapper">
            <ActionBar title="Title Here" />
            <BalanceBar balance={balance} />
            <div className="app-content">
              <List items={items} />
          </div>
        </div>
      </div>
    );
  }
});


답변

한 사용의 단점 componentDidUpdate, 또는 componentDidMountDOM을 요소가 그려지 완료하기 전에이 실제로 실행하는 것입니다,하지만 그들은에서 통과 한 후에 브라우저의 DOM에 반응한다.

예를 들어 node.scrollHeight를 렌더링 된 node.scrollTop으로 설정해야한다면 React의 DOM 요소로는 충분하지 않을 수 있습니다. 높이를 얻기 위해 요소가 칠해질 때까지 기다려야합니다.

해결책:

requestAnimationFrame새로 렌더링 된 객체를 페인팅 한 후 코드가 실행되도록하는 데 사용

scrollElement: function() {
  // Store a 'this' ref, and
  var _this = this;
  // wait for a paint before running scrollHeight dependent code.
  window.requestAnimationFrame(function() {
    var node = _this.getDOMNode();
    if (node !== undefined) {
      node.scrollTop = node.scrollHeight;
    }
  });
},
componentDidMount: function() {
  this.scrollElement();
},
// and or
componentDidUpdate: function() {
  this.scrollElement();
},
// and or
render: function() {
  this.scrollElement()
  return [...]


답변

내 경험상 window.requestAnimationFrameDOM이에서 완전히 리플 로우 / 리플 로우 완료되었는지 확인하기에 충분하지 않았습니다 componentDidMount. componentDidMount호출 직후 DOM에 액세스하는 코드를 실행 중이며 단독으로 사용 window.requestAnimationFrame하면 DOM에 요소가 존재합니다. 그러나 리플 로우가 아직 발생하지 않았으므로 요소의 치수에 대한 업데이트는 아직 반영되지 않습니다.

이 작업을 수행 할 수있는 진정으로 신뢰할 수있는 유일한 방법 은 다음 프레임의 렌더에 등록하기 전에 React의 현재 호출 스택이 지워지도록 메소드를 a setTimeout와 a 로 감싸는 것 window.requestAnimationFrame입니다.

function onNextFrame(callback) {
    setTimeout(function () {
        requestAnimationFrame(callback)
    })
}

이것이 왜 발생하고 필요한지 추측 해야하는 경우 React batching DOM 업데이트를보고 현재 스택이 완료 될 때까지 DOM에 변경 사항을 실제로 적용하지 않을 수 있습니다.

궁극적으로 React 콜백 후에 발생하는 코드에서 DOM 측정을 사용하는 경우이 방법을 사용하는 것이 좋습니다.


답변

새로운 Hook 메소드 로이 질문을 약간 업데이트하려면 useEffect후크 를 사용하면됩니다 .

import React, { useEffect } from 'react'

export default function App(props) {

     useEffect(() => {
         // your post layout code (or 'effect') here.
         ...
     },
     // array of variables that can trigger an update if they change. Pass an
     // an empty array if you just want to run it once after component mounted. 
     [])
}

또한 레이아웃 페인트 전에 실행하려면 useLayoutEffect후크를 사용하십시오 .

import React, { useLayoutEffect } from 'react'

export default function App(props) {

     useLayoutEffect(() => {
         // your pre layout code (or 'effect') here.
         ...
     }, [])
}


답변

상태를 변경 한 다음 setState 콜백 에서 계산을 수행 할 수 있습니다 . React 문서에 따르면, 이것은 “업데이트가 적용된 후에 발생하도록 보장됩니다”.

이것은 componentDidMount생성자가 아닌 코드의 크기 조정 이벤트 핸들러와 같이 또는 다른 곳에서 수행해야합니다 .

이것은 좋은 대안 window.requestAnimationFrame이며 여기에서 일부 사용자가 언급 한 문제가 없습니다 ( setTimeout복합하거나 여러 번 호출해야 함). 예를 들면 다음과 같습니다.

class AppBase extends React.Component {
    state = {
        showInProcess: false,
        size: null
    };

    componentDidMount() {
        this.setState({ showInProcess: true }, () => {
            this.setState({
                showInProcess: false,
                size: this.calculateSize()
            });
        });
    }

    render() {
        const appStyle = this.state.showInProcess ? { visibility: 'hidden' } : null;

        return (
            <div className="wrapper">
                ...
                <div className="app-content" style={appStyle}>
                    <List items={items} />
                </div>
                ...
            </div>
        );
    }
}


답변

나는이 솔루션이 더럽다고 생각하지만 여기에 우리는 간다.

componentDidMount() {
    this.componentDidUpdate()
}

componentDidUpdate() {
    // A whole lotta functions here, fired after every render.
}

이제 난 그냥 여기 앉아서 아래로 투표를 기다립니다.


답변

React에는 getInitialState, getDefaultProps, componentWillMount, componentDidMount 등 을 포함하지만 이에 국한되지 않는 이러한 상황에서 도움이되는 수명주기 메소드가 거의 없습니다 .

귀하의 경우와 DOM 요소와 상호 작용 해야하는 경우 dom이 준비 될 때까지 기다려야하므로 다음과 같이 componentDidMount 를 사용하십시오 .

/** @jsx React.DOM */
var List = require('../list');
var ActionBar = require('../action-bar');
var BalanceBar = require('../balance-bar');
var Sidebar = require('../sidebar');
var AppBase = React.createClass({
  componentDidMount: function() {
    ReactDOM.findDOMNode(this).height = /* whatever HEIGHT */;
  },
  render: function () {
    return (
      <div className="wrapper">
        <Sidebar />
        <div className="inner-wrapper">
          <ActionBar title="Title Here" />
          <BalanceBar balance={balance} />
          <div className="app-content">
            <List items={items} />
          </div>
        </div>
      </div>
    );
  }
});

module.exports = AppBase;

또한 반응의 수명주기에 대한 자세한 내용은 다음 링크를 참조하십시오.
https://facebook.github.io/react/docs/state-and-lifecycle.html

getInitialState, getDefaultProps, componentWillMount, componentDidMount