我有一个应用程序,我需要动态设置一个元素的高度(让我们说“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()中使用类似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));
}
使用componentDidUpdate或componentDidMount的一个缺点是,它们实际上是在dom元素完成绘制之前执行的,而是在它们从React传递到浏览器的dom之后。
比如说,如果你需要set node。到呈现节点的scrollHeight。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 [...]
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
只是用新的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.
...
}, [])
}