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

Posted

技术标签:

【中文标题】反应:渲染后无法获得正确的元素宽度【英文标题】:React: Can't get correct width of element after render 【发布时间】:2015-09-07 20:14:14 【问题描述】:

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

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

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

这是一个子组件,如果有任何区别,父组件会在 iFrame 中呈现,并且 iFrame 的宽度远超过 600 像素。

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', '');
    
  

);

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;
  

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

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

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

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

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

更新:2015 年 6 月 24 日 澄清一下,这是我想要实现的marquee effect,仅当文本大于其容器时,因为它不更大时滚动它是毫无意义的。

这里还有一个链接,指向来自 React 团队的 Github Issue,讲述了为什么 React 在浏览器绘制之前呈现。 - 既然如此,我想知道如何可靠地获取相关元素的宽度。

【问题讨论】:

我不是 100% 清楚你的目标是什么。你想防止你的内容溢出容器吗?你遇到了什么问题? 不,我希望我的内容在其大于其容器时自动在其容器内水平滚动,就像一个选框效果,类似于 jsfiddle.net/jonathansampson/xxuxd 【参考方案1】:

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

【讨论】:

这是我的情况,我浪费了将近一天的时间试图找到可能导致此问题的原因,但从未想过在 css 之后加载 js,这是在 js 之前加载 css 的方法:@ 987654321@【参考方案2】:

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

有一个名为React-Context 的库可以完全按照您的要求进行操作。你可以将它的 api 暴露给你的包装组件,然后就可以监听虚拟 dom 中组件上的事件。

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

【讨论】:

感谢@newswim 的更新。我不再积极研究这个了,但你的解决方案听起来很有趣!如果我重新开始工作,我将实施您的解决方案并在这里报告。谢谢【参考方案3】:

你不应该在 render() 方法中调用this.initMarquee();

一般来说,您根本不应该在 render() 方法中使用 DOM。

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

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

【讨论】:

感谢您发现@ByScripts - 你是对的,我已经这样设置了,不幸的是它没有工作。如链接答案中所述,使用 requestAnimationFrame 是因为:“使用 componentDidUpdate 或 componentDidMount 的一个缺点是它们实际上是在绘制 dom 元素之前执行的,但在它们从 React 传递到浏览器的 DOM 之后。 "

以上是关于反应:渲染后无法获得正确的元素宽度的主要内容,如果未能解决你的问题,请参考以下文章

在 useQuery 后反应组件不重新渲染

状态更改后反应无法正确渲染

反应元素宽度动画

无法从反应光滑中获得所需的结果

没有获得 UIAccessibilityElement 的正确坐标

为啥没有滚动条我无法获得容器宽度?