首页 文章

反应“渲染后”代码?

提问于
浏览
265

我有一个应用程序,我需要动态设置元素的高度(让我们说“app-content”) . 它采用应用程序的“chrome”的高度并减去它,然后将“app-content”的高度设置为在这些约束内适合100% . 使用vanilla JS,jQuery或Backbone视图这是非常简单的,但我很难弄清楚在React中执行此操作的正确流程是什么?

下面是一个示例组件 . 我希望能够将 app-content 的高度设置为窗口的100%减去 ActionBarBalanceBar 的大小,但是如何知道何时渲染所有内容以及将计算内容放在此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;

13 回答

  • 0

    https://facebook.github.io/react/docs/react-component.html#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>
        );
      }
    });
    
  • 2

    渲染后,您可以像下面一样指定高度,并可以指定相应反应组件的高度 .

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

    或者您可以使用sass指定每个反应组件的高度 . 指定前2个具有固定宽度的反应分量主分区,然后使用auto指定第三个分量主分区的高度 . 因此,根据第三个div的内容,将分配高度 .

  • 190

    来自ReactDOM.render()文档:

    如果提供了可选回调,则在呈现或更新组件后将执行该回调 .

  • 0

    当我需要打印接收大量数据并在画布上绘制的反应组件时,我有奇怪的情况 . 我已经尝试了所有提到的方法,其中没有它们可靠地为我工作,在setTimeout内的requestAnimationFrame我在20%的时间得到空画布,所以我做了以下:

    nRequest = n => range(0,n).reduce(
    (acc,val) => () => requestAnimationFrame(acc), () => requestAnimationFrame(this.save)
    );
    

    基本上我做了一个requestAnimationFrame的链,不确定是不是这个好主意但是到目前为止我在100%的情况下工作(我使用30作为n变量的值) .

  • 3

    您可以更改状态,然后在setState callback中进行计算 . 根据React文档,这是"guaranteed to fire after the update has been applied" .

    这应该在 componentDidMount 或代码中的其他地方(比如在resize事件处理程序中)而不是在构造函数中完成 .

    这是 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>
            );
        }
    }
    
  • 1

    我实际上遇到了类似行为的问题,我在一个Component中使用它的id属性渲染一个视频元素,所以当RenderDOM.render()结束时,它会加载一个插件,需要id才能找到占位符而无法找到它 .

    componentDidMount()里面的0ms的setTimeout修复了它:)

    componentDidMount() {
        if (this.props.onDidMount instanceof Function) {
            setTimeout(() => {
                this.props.onDidMount();
            }, 0);
        }
    }
    
  • 5

    根据我的经验, window.requestAnimationFrame 不足以确保从 componentDidMount 完全渲染/重新完成DOM . 我运行的代码在 componentDidMount 调用之后立即访问DOM,并且仅使用 window.requestAnimationFrame 将导致元素存在于DOM中;然而,由于尚未发生回流,因此对元素's dimensions aren'的更新反映出来了 .

    唯一真正可行的方法是将我的方法包装在 setTimeoutwindow.requestAnimationFrame 中,以确保React 's current call stack gets cleared before registering for the next frame'呈现 .

    function onNextFrame(callback) {
        setTimeout(function () {
            window.requestAnimationFrame(callback)
        }, 0)
    }
    

    如果我不得不推测为什么会发生这种情况/必要,我可以看到React批处理DOM更新,而不是直到当前堆栈完成后才将更改应用到DOM .

    最后,如果您在React回调之后使用的代码中使用DOM测量,则可能需要使用此方法 .

  • 2

    使用 componentDidUpdatecomponentDidMount 的一个缺点是它们实际上是在完成dom元素绘制之前执行的,但是在它们之后执行它们 .

    比如说,如果你需要将node.scrollHeight设置为渲染的node.scrollTop,那么React的DOM元素可能还不够 . 你需要等到元素完成后才能达到它们的高度 .

    解:

    使用 requestAnimationFrame 确保在绘制新渲染的对象后运行代码

    scrollElement: function() {
      //store a this ref, and
      var _this = this;
      //wait for a paint to do scrolly stuff
      window.requestAnimationFrame(function() {
        var node = _this.getDOMNode();
        if (node !== undefined) {
          //and scroll them!
          node.scrollTop = node.scrollHeight;
        }
      });
    },
    componentDidMount: function() {
      this.scrollElement();
    },
    // and or
    componentDidUpdate: function() {
      this.scrollElement();
    },
    // and or
    render: function() {
      this.scrollElement()
      return [...]
    
  • 1

    使用 ES6 类而不是 React.createClass 进行一些更新

    import React, { Component } from 'react';
    
    class SomeComponent extends Component {
      constructor(props) {
        super(props);
        // this code might be called when there is no element avaliable in `document` yet (eg. initial render)
      }
    
      componentDidMount() {
        // this code will be always called when component is mounted in browser DOM ('after render')
      }
    
      render() {
        return (
          <div className="component">
            Some Content
          </div>
        );
      }
    }
    

    另外 - 检查React组件生命周期方法:https://facebook.github.io/react/docs/react-component.html#the-component-lifecycle

    每个组件都有很多类似于 componentDidMount 的方法 .

    • componentWillUnmount() - 组件即将从浏览器DOM中删除
  • 6

    我遇到了同样的问题 .

    在大多数情况下使用 componentDidMount() 中的hack-ish setTimeout(() => { }, 0) 工作 .

    但不是在特殊情况下;我不想使用 ReachDOM findDOMNode ,因为文档说:

    注意:findDOMNode是用于访问底层DOM节点的转义填充 . 在大多数情况下,不鼓励使用此逃生舱,因为它会刺穿组件抽象 .

    (来源:https://facebook.github.io/react/docs/react-dom.html#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));
    }
    
  • 69

    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;
    

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

  • 0

    我觉得这个解决方案很脏,但我们走了:

    componentDidMount() {
        this.componentDidUpdate()
    }
    
    componentDidUpdate() {
        // A whole lotta functions here, fired after every render.
    }
    

    现在我只是坐在这里等待下来的选票 .

  • 232

    对我来说,没有 window.requestAnimationFramesetTimeout 的组合产生一致的结果 . 有时它起作用,但并非总是 - 或者有时候为时已晚 .

    我根据需要循环 window.requestAnimationFrame 来修复它 .
    (通常为0或2-3次)

    关键是 diff > 0 :这里我们可以确切地确定页面何时更新 .

    // Ensure new image was loaded before scrolling
    if (oldH > 0 && images.length > prevState.images.length) {
        (function scroll() {
            const newH = ref.scrollHeight;
            const diff = newH - oldH;
    
            if (diff > 0) {
                const newPos = top + diff;
                window.scrollTo(0, newPos);
            } else {
                window.requestAnimationFrame(scroll);
            }
        }());
    }
    

相关问题