javascript 反应:渲染后无法获得正确的元素宽度

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/30978911/
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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-28 13:03:22  来源:igfitidea点击:

React: Can't get correct width of element after render

javascriptiframewidthreactjs

提问by Giles Butler

I'm trying to setup a Marquee in React if a piece of text is greater than its container but I can't get the correct width of the container, even after the component has rendered.

如果一段文本大于其容器,我正在尝试在 React 中设置一个选取框,但我无法获得容器的正确宽度,即使在组件呈现之后也是如此。

I read in another answer React “after render” code?that you have to use requestAnimationFrame which I'm trying and it's still not working.

我在另一个答案中读到React “after render” 代码?您必须使用我正在尝试的 requestAnimationFrame 但它仍然无法正常工作。

If I log the width of the container it shows a width of 147px which is set using min-width in the stylesheet but the correct width should be 320px which is set using a media query when the screens min-width is 600px.

如果我记录容器的宽度,它会显示 147px 的宽度,这是在样式表中使用 min-width 设置的,但正确的宽度应该是 320px,当屏幕 min-width 为 600px 时,它是使用媒体查询设置的。

This is a child component, the parent is rendered inside an iFrame if it makes any difference and the iFrame's width is well over 600px.

这是一个子组件,如果它有任何不同,父组件会在 iFrame 中呈现,并且 iFrame 的宽度远远超过 600px。

The JS:

JS:

module.exports = React.createClass({

  componentDidUpdate: function () {
    // Setup marquee
    this.initMarquee();
  },

  render: function () {
    // Setup marquee
    this.initMarquee();

    var artistName = this.props.artist.artistName;
    var trackName  = this.props.track.trackName;

    return (
      <div className="MV-player-trackData no-select" ref="mvpTrackData">
        <div className="MV-player-trackData-marquee-wrap" ref="mvpMarqueeWrap">
          <div className="MV-player-trackData-marquee" ref="mvpMarquee">
            <a className="MV-player-trackData-link no-select" href={this.props.storeUrl} target="_blank">
              <span id="mvArtistName">{artistName}</span> &ndash; <span id="mvTrackName">{trackName}</span>
            </a>
          </div>
        </div>
      </div>
    )
  },

  initMarquee: function () {
    if ( typeof requestAnimationFrame !== 'undefined' ) {
      //store a this ref, and
      var self = this;
      //wait for a paint to setup marquee
      window.requestAnimationFrame(function() {
        self.marquee();
      });
    }
    else {
      // Suport older browsers
      window.setTimeout(this.marquee, 2000);
    }
  },

  marquee: function () {
    var marquee     = React.findDOMNode(this.refs.mvpMarquee);
    var marqueeWrap = React.findDOMNode(this.refs.mvpMarqueeWrap);

    // If the marquee is greater than its container then animate it
    if ( marquee.clientWidth > marqueeWrap.clientWidth ) {
      marquee.className += ' is-animated';
    }
    else {
      marquee.className = marquee.className.replace('is-animated', '');
    }
  }

});

The CSS:

CSS:

.MV-player-trackData-marquee-wrap {
  height: 20px;
  line-height: 20px;
  min-width: 147px;
  overflow: hidden;
  position: relative;
  width: 100%;

  @media only screen and (min-width : 600px) {
    min-width: 320px;
  }
}

I've tried a number of different solutions including laidoutand react-component-width-mixinbut neither of them work. I tried react component width mixin because in another part of my app I'm trying to get the value of window.innerWidthbut that also returns 0 after rendering, unless I set a timeout for around 2 seconds, unfortunately though sometimes 2 seconds isn't long enough due to data loading and other callbacks so this can brake easily.

我尝试了许多不同的解决方案,包括layoutreact-component-width-mixin,但它们都不起作用。我尝试了 react component width mixin 因为在我的应用程序的另一部分我试图获取的值window.innerWidth但在渲染后也返回 0,除非我将超时设置为大约 2 秒,不幸的是有时 2 秒不够长由于数据加载和其他回调,所以这很容易刹车。

Any help is really appreciated. Thanks.

任何帮助都非常感谢。谢谢。

Update:One of the answers correctly pointed out i should be calling this.initMarquee();inside componentDidMountwhich I was doing, unfortunately I pasted the wrong code from when I was testing to see if it made a difference calling it inside render. The correct code looks like this:

更新:其中一个答案正确地指出我应该this.initMarquee();componentDidMount我正在做的内部调用,不幸的是我粘贴了错误的代码,因为我在测试时粘贴了错误的代码,以查看在内部调用它是否有所不同render。正确的代码如下所示:

componentDidMount: function () {
  // Setup marquee
  this.initMarquee();
},

Unfortunately this doesn't work either, I still receive the incorrect width for marqueeWrap.

不幸的是,这也不起作用,我仍然收到不正确的 marqueeWrap 宽度。

Update: 24/06/2015Just to clarify, this is the marquee effectI'm trying to achieve, only when the text is bigger than its container as it's pointless scrolling it when it is not bigger.

更新:24/06/2015只是为了澄清,这是我想要实现的选取框效果,仅当文本大于其容器时,因为在文本不大时滚动它是毫无意义的。

Also here is a link to a Github Issuefrom the React team speaking about why React renders before the browser paints. - So as that is the case, I want to know how do I reliably get the width of the element in question.

这里还有一个来自 React 团队的Github 问题的链接,它谈到了为什么 React 在浏览器绘制之前呈现。- 在这种情况下,我想知道如何可靠地获得相关元素的宽度。

回答by Zaptree

One possible problem that can occur is that your CSS has not loaded yet when componentDidMount fires. This can happen with webpack and including the css in your bundle that also contains your js even if you have included the css before your component in the project.

可能发生的一个问题是,当 componentDidMount 触发时,您的 CSS 尚未加载。这可能发生在 webpack 中,并且将 css 包含在你的包中,即使你已经在项目中的组件之前包含了 css。

回答by newswim

There are several issues, as you've pointed out, in dealing with the virtual DOM. You definitely don't want to be attempting to use jQuery to manipulate DOM nodes and React is likely to scream at your for attempting to.

正如您所指出的,在处理虚拟 DOM 时有几个问题。您绝对不想尝试使用 jQuery 来操作 DOM 节点,而 React 可能会因为您的尝试而对您大喊大叫。

There's a library called React-Contextwhich would do exactly what you're asking. You would expose its api to your wrapper component and then be able to listen to events on components within the virtual dom.

有一个名为React-Context的库,它可以完全满足您的要求。您将其 api 公开给您的包装器组件,然后能够侦听虚拟 dom 内的组件上的事件。

This library is a little dusty, however it should work with the code sample you shared.

这个库有点脏,但是它应该可以与您共享的代码示例一起使用。

回答by Chris Hawkes

I'm confused by this question, what are you trying to do with the width?

我对这个问题感到困惑,你想用宽度做什么?

That aside, React is only JavaScript, therefore you should be able to fire some code on your componentDidMount function which is executed after the element has a physical space in your browsers dom. Therefore, just like any JavaScript code you will be able to access that div and get it's width etc...

除此之外,React 只是 JavaScript,因此您应该能够在 componentDidMount 函数上触发一​​些代码,该函数在元素在浏览器 dom 中具有物理空间后执行。因此,就像任何 JavaScript 代码一样,您将能够访问该 div 并获取它的宽度等...

JavaScript way...

JavaScript 方式...

document.getElementById("mydiv").offsetWidth;

jQuery way...

jQuery 方式...

$('#mydiv').width();

If the problem you are trying to solve is dynamically changing the width of a div because it does not look right after it's rendered etc... this should really be done using CSS.

如果您要解决的问题是动态更改 div 的宽度,因为它在呈现后看起来不正确等等……这真的应该使用 CSS 来完成。

回答by ByScripts

You should not call this.initMarquee();in the render() method.

您不应调用this.initMarquee();render() 方法。

In general, you should not work with the DOM at all in the render()method.

通常,您根本不应该在render()方法中使用 DOM 。

Try to call this.initMarquee();in the componentDidMountmethod.

尝试调用this.initMarquee();componentDidMount方法。

(and I really don't understand the usage of requestAnimationFramein this case)

(我真的不明白requestAnimationFrame在这种情况下的用法)