Javascript 反应“渲染后”代码?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/26556436/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
React "after render" code?
提问by Oscar Godson
I have an app where I need to set the height of an element (lets say "app-content") dynamically. It takes the height of the "chrome" of the app and subtracts it and then sets the height of the "app-content" to fit 100% within those constraints. This is super simple with vanilla JS, jQuery, or Backbone views, but I'm struggling to figure out what the right process would be for doing this in React?
我有一个应用程序,我需要在其中动态设置元素的高度(比如“app-content”)。它获取应用程序“chrome”的高度并减去它,然后将“应用程序内容”的高度设置为 100% 适合这些约束。这对于 vanilla JS、jQuery 或 Backbone 视图来说非常简单,但我正在努力弄清楚在 React 中执行此操作的正确过程是什么?
Below is an example component. I want to be able to set app-content's height to be 100% of the window minus the size of the ActionBarand BalanceBar, but how do I know when everything is rendered and where would I put the calculation stuff in this React Class?
下面是一个示例组件。我希望能够将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;
采纳答案by Harborhoffer
This method is called once after your component is rendered. So your code would look like so.
此方法在您的组件呈现后调用一次。所以你的代码看起来像这样。
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>
);
}
});
回答by Graham P Heath
One drawback of using componentDidUpdate, or componentDidMountis that they are actually executed before the dom elements are done being drawn, but after they've been passed from React to the browser's DOM.
使用componentDidUpdate, or 的一个缺点componentDidMount是它们实际上是在绘制 dom 元素之前执行的,但在它们从 React 传递到浏览器的 DOM 之后执行。
Say for example if you needed set node.scrollHeight to the rendered node.scrollTop, then React's DOM elements may not be enough. You need to wait until the elements are done being painted to get their height.
例如,如果您需要将 node.scrollHeight 设置为渲染的 node.scrollTop,那么 React 的 DOM 元素可能还不够。您需要等到元素完成绘制才能获得它们的高度。
Solution:
解决方案:
Use requestAnimationFrameto ensure that your code is run after the painting of your newly rendered object
使用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 [...]
回答by Elliot Chong
In my experience window.requestAnimationFramewasn't enough to ensure that the DOM had been fully rendered / reflow-complete from componentDidMount. I have code running that accesses the DOM immediately after a componentDidMountcall and using solely window.requestAnimationFramewould result in the element being present in the DOM; however, updates to the element's dimensions aren't reflected yet since a reflow hasn't yet occurred.
根据我的经验window.requestAnimationFrame,这不足以确保 DOM 已从componentDidMount. 我运行的代码在componentDidMount调用后立即访问 DOM ,单独使用window.requestAnimationFrame会导致元素出现在 DOM 中;但是,由于尚未发生回流,因此尚未反映对元素尺寸的更新。
The only truly reliable way for this to work was to wrap my method in a setTimeoutand a window.requestAnimationFrameto ensure React's current call stack gets cleared before registering for the next frame's render.
唯一真正可靠的方法是将我的方法包装在 asetTimeout和 a 中,window.requestAnimationFrame以确保在注册下一帧的渲染之前清除 React 的当前调用堆栈。
function onNextFrame(callback) {
setTimeout(function () {
requestAnimationFrame(callback)
})
}
If I had to speculate on why this is occurring / necessary I could see React batching DOM updates and not actually applying the changes to the DOM until after the current stack is complete.
如果我不得不推测为什么会发生这种情况/这是必要的,我可以看到 React 批处理 DOM 更新,并且直到当前堆栈完成后才实际将更改应用于 DOM。
Ultimately, if you're using DOM measurements in the code you're firing after the React callbacks you'll probably want to use this method.
最终,如果您在 React 回调之后触发的代码中使用 DOM 测量,您可能希望使用此方法。
回答by P Fuster
Just to update a bit this question with the new Hook methods, you can simply use the useEffecthook:
只是用新的 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.
[])
}
Also if you want to run before the layout paint use the useLayoutEffecthook:
此外,如果您想在布局绘制之前运行,请使用useLayoutEffect钩子:
import React, { useLayoutEffect } from 'react'
export default function App(props) {
useLayoutEffect(() => {
// your pre layout code (or 'effect') here.
...
}, [])
}
回答by Joseph238
You can change the state and then do your calculations in the setState callback. According to the React documentation, this is "guaranteed to fire after the update has been applied".
您可以更改状态,然后在setState 回调中进行计算。根据 React 文档,这是“保证在应用更新后触发”。
This should be done in componentDidMountor somewhere else in the code (like on a resize event handler) rather than in the constructor.
这应该componentDidMount在代码中或其他地方(如在调整大小事件处理程序上)而不是在构造函数中完成。
This is a good alternative to window.requestAnimationFrameand it does not have the issues some users have mentioned here (needing to combine it with setTimeoutor call it multiple times). For example:
这是一个很好的替代方案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>
);
}
}
回答by Jaakko Karhu
I feel that this solution is dirty, but here we go:
我觉得这个解决方案很脏,但我们继续:
componentDidMount() {
this.componentDidUpdate()
}
componentDidUpdate() {
// A whole lotta functions here, fired after every render.
}
Now I am just going to sit here and wait for the down votes.
现在我只是要坐在这里等待反对票。
回答by Alireza
React has few lifecycle methods which help in these situations, the lists including but not limited to getInitialState, getDefaultProps, componentWillMount, componentDidMountetc.
React 几乎没有生命周期方法可以帮助这些情况,列表包括但不限于getInitialState、getDefaultProps、componentWillMount、componentDidMount等。
In your case and the cases which needs to interact with the DOM elements, you need to wait till the dom is ready, so use componentDidMountas below:
在你的情况和需要与 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;
Also for more information about lifecycle in react you can have look the below link: https://facebook.github.io/react/docs/state-and-lifecycle.html
此外,有关反应生命周期的更多信息,您可以查看以下链接:https: //facebook.github.io/react/docs/state-and-lifecycle.html
回答by ron.camaron
I ran into the same problem.
我遇到了同样的问题。
In most scenarios using the hack-ish setTimeout(() => { }, 0)in componentDidMount()worked.
在大多数情况下,使用 hack-ishsetTimeout(() => { }, 0)是componentDidMount()有效的。
But not in a special case; and I didn't want to use the ReachDOM findDOMNodesince the documentation says:
但不是在特殊情况下;我不想使用,ReachDOM findDOMNode因为文档说:
Note: findDOMNode is an escape hatch used to access the underlying DOM node. In most cases, use of this escape hatch is discouraged because it pierces the component abstraction.
注意:findDOMNode 是一个用于访问底层 DOM 节点的逃生舱口。在大多数情况下,不鼓励使用此逃生舱口,因为它穿透了组件抽象。
(Source: findDOMNode)
(来源:findDOMNode)
So in that particular component I had to use the componentDidUpdate()event, so my code ended up being like this:
所以在那个特定的组件中我不得不使用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);
}
And then:
进而:
componentDidUpdate() {
this.updateDimensions();
}
Finally, in my case, I had to remove the listener created in componentDidMount:
最后,就我而言,我必须删除在以下位置创建的侦听器componentDidMount:
componentWillUnmount() {
window.removeEventListener("resize", this.updateDimensions.bind(this));
}
回答by prudhvi seeramreddi
After render, you can specify the height like below and can specify the height to corresponding react components.
渲染后,您可以像下面这样指定高度,并可以为相应的反应组件指定高度。
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>
);`
}
or you can specify the height of the each react component using sass. Specify first 2 react component main div's with fixed width and then the third component main div's height with auto. So based on the third div's content the height will be assigned.
或者您可以使用 sass 指定每个反应组件的高度。指定前 2 个反应组件主 div 的宽度固定,然后第三个组件主 div 的高度自动。因此,将根据第三个 div 的内容分配高度。
回答by Barceyken
I'm actually having a trouble with similar behaviour, I render a video element in a Component with it's id attribute so when RenderDOM.render() ends it loads a plugin that needs the id to find the placeholder and it fails to find it.
我实际上遇到了类似行为的问题,我在具有 id 属性的组件中渲染了一个视频元素,因此当 RenderDOM.render() 结束时,它会加载一个需要 id 来查找占位符的插件,但它找不到它。
The setTimeout with 0ms inside the componentDidMount() fixed it :)
componentDidMount() 中的 setTimeout 为 0ms 修复了它:)
componentDidMount() {
if (this.props.onDidMount instanceof Function) {
setTimeout(() => {
this.props.onDidMount();
}, 0);
}
}


