我有一个应用程序,我需要动态设置一个元素的高度(让我们说“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;

当前回答

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

其他回答

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;

此外,有关react生命周期的更多信息,您可以查看下面的链接: https://facebook.github.io/react/docs/state-and-lifecycle.html

ReactDOM.render()文档:

如果提供了可选的回调,它将在 组件被呈现或更新。

您可以更改状态,然后在setState回调中进行计算。根据React文档,这是“保证在更新应用后触发”。

这应该在componentDidMount或代码中的其他地方完成(比如在调整大小事件处理程序上),而不是在构造函数中完成。

这是窗户的好替代品。它没有一些用户在这里提到的问题(需要结合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>
        );
    }
}

对我来说,componentDidUpdate单独或窗口。requestAnimationFrame本身并不能解决问题,但是下面的代码可以工作。

// Worked but not succinct
    componentDidUpdate(prevProps, prevState, snapshot) {
        if (this.state.refreshFlag) {  // in the setState for which you want to do post-rendering stuffs, set this refreshFlag to true at the same time, to enable this block of code.
            window.requestAnimationFrame(() => {
                this.setState({
                    refreshFlag: false   // Set the refreshFlag back to false so this only runs once.
                });
                something = this.scatterChart.current.canvas
                    .toDataURL("image/png");  // Do something that need to be done after rendering is finished. In my case I retrieved the canvas image.
            });
        }
    }

后来我测试requestAnimationFrame注释,它仍然完美地工作:

// The best solution I found
    componentDidUpdate(prevProps, prevState, snapshot) {
        if (this.state.refreshFlag) {  // in the setState for which you want to do post-rendering stuffs, set this refreshFlag to true at the same time, to enable this block of code.
            // window.requestAnimationFrame(() => {
                this.setState({
                    refreshFlag: false   // Set the refreshFlag back to false so this only runs once.
                });
                something = this.scatterChart.current.canvas
                    .toDataURL("image/png");  // Do something that need to be done after rendering is finished. In my case I retrieved the canvas image.
            // });
        }
    }

我不确定这是否只是一个巧合,额外的setState诱导时间延迟,以便在检索图像时,绘图已经完成(我将得到旧的画布图像,如果我删除setState)。

或者更可能的是,这是因为setState需要在所有内容呈现之后执行,所以它强制等待呈现完成。

我倾向于相信后者,因为根据我的经验,在我的代码中连续调用setState会导致每一个都在最后一个渲染完成后才被触发。

最后,我测试了以下代码。如果this.setState ({});不更新组件,但等到渲染完成,这将是最终的最佳解决方案,我认为。然而,它失败了。即使传递一个空的{},setState()仍然更新组件。

// This one failed!
    componentDidUpdate(prevProps, prevState, snapshot) {
        // if (this.state.refreshFlag) {
            // window.requestAnimationFrame(() => {
                this.setState({});
                something = this.scatterChart.current.canvas
                    .toDataURL("image/png");
            // });
        // }
    }

在尝试了上面所有建议的解决方案后,没有运气,我发现我的一个元素在中间有CSS过渡,这就是为什么我不能得到正确的计算几何后道具改变。 所以我不得不使用onTransitionEnd监听器等待片刻,当尝试获得容器元素的DOM高度计算。 希望这能节省一些人的工作日,哈哈。