我有一个应用程序,我需要动态设置一个元素的高度(让我们说“app-content”)。它取应用程序的“chrome”的高度并减去它,然后设置“app-content”的高度以100%符合这些限制。这是超级简单的香草JS, jQuery,或骨干视图,但我努力弄清楚正确的过程将在React中这样做?

下面是一个示例组件。我想要能够设置应用内容的高度为窗口的100%减去动作栏和BalanceBar的大小,但我怎么知道什么时候所有的渲染和哪里我将把计算的东西在这个反应类?

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

当前回答

只是用新的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.
         ...
     }, [])
}

其他回答

我也遇到了同样的问题。

在大多数情况下,在componentDidMount()中使用类似hack的setTimeout(() =>{}, 0)是有效的。

但不是在特殊情况下;我不想使用ReachDOM findDOMNode,因为文档说:

注意:findDOMNode是一个用于访问底层DOM的escape hatch 节点。在大多数情况下,不鼓励使用这个逃生口,因为 它穿透了组件抽象。

(来源:findDOMNode)

所以在那个特定的组件中,我必须使用componentDidUpdate()事件,所以我的代码最终是这样的:

componentDidMount() {
    // feel this a little hacky? check this: http://stackoverflow.com/questions/26556436/react-after-render-code
    setTimeout(() => {
       window.addEventListener("resize", this.updateDimensions.bind(this));
       this.updateDimensions();
    }, 0);
}

然后:

componentDidUpdate() {
    this.updateDimensions();
}

最后,在我的例子中,我必须删除componentDidMount中创建的侦听器:

componentWillUnmount() {
    window.removeEventListener("resize", this.updateDimensions.bind(this));
}

渲染之后,你可以像下面这样指定高度,并可以为相应的react组件指定高度。

render: function () {
    var style1 = {height: '100px'};
    var style2 = { height: '100px'};

   //window. height actually will get the height of the window.
   var hght = $(window).height();
   var style3 = {hght - (style1 + style2)} ;

    return (
      <div className="wrapper">
        <Sidebar />
        <div className="inner-wrapper">
          <ActionBar style={style1} title="Title Here" />
          <BalanceBar style={style2} balance={balance} />
          <div className="app-content" style={style3}>
            <List items={items} />
          </div>
        </div>
      </div>
    );`
  }

或者你可以使用sass指定每个react组件的高度。指定前2个react组件主div的固定宽度,然后第三个组件主div的高度为auto。因此,根据第三个div的内容,高度将被分配。

在我的经验窗口。requestAnimationFrame不足以确保DOM已经从componentDidMount完全渲染/ reflow-complete。我有代码运行,访问DOM后立即componentDidMount调用和使用单独的窗口。requestAnimationFrame将导致元素出现在DOM中;然而,对元素尺寸的更新还没有反映出来,因为回流还没有发生。

唯一真正可靠的方法是将我的方法包装在一个setTimeout和一个窗口中。requestAnimationFrame以确保React的当前调用堆栈在注册下一帧渲染之前被清除。

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

如果我不得不推测为什么这是发生的/必要的,我可以看到React批处理DOM更新,直到当前堆栈完成后才实际应用更改到DOM。

最终,如果你在React回调后触发的代码中使用DOM度量,你可能会想要使用这个方法。

我不会假装我知道为什么这个函数可以工作,但是window。当我需要在useEffect中使用Ref访问DOM元素时,getComputedStyle 100%的时间为我工作-我只能假设它将与componentDidMount一起工作。

我把它放在useEffect代码的顶部,它似乎迫使效果等待元素被绘制,然后再继续下一行代码,但没有任何明显的延迟,如使用setTimeout或异步睡眠函数。如果没有这个,当我试图访问Ref元素时,它将返回为未定义。

const ref = useRef(null);

useEffect(()=>{
    window.getComputedStyle(ref.current);
    // Next lines of code to get element and do something after getComputedStyle().
});

return(<div ref={ref}></div>);

我建议你使用钩子。 它们可以从16.8.0版本开始使用。

你可以在react的官方文档中查看这个钩子的行为。

就像这样:

import React, { useEffect } from 'react'


const AppBase = ({ }) => {

    useEffect(() => {
        // set el height and width etc.
    }, [])

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

export default AppBase