反应“渲染后”代码?

2020/09/29 12:41 · javascript ·  · 0评论

我有一个应用程序,需要动态设置元素的高度(让我们说“ app-content”)。它获取应用程序“ chrome”的高度,然后减去它的高度,然后将“ app-content”的高度设置为在这些约束内适合100%。这对于香草JS,jQuery或Backbone视图来说非常简单,但我正在努力弄清楚在React中执行此操作的正确过程是什么?

以下是一个示例组件。我希望能够将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;

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

使用componentDidUpdate或的一个缺点componentDidMount是,它们实际上是在绘制dom元素之前,但是在将它们从React传递到浏览器的DOM之后才执行的。

举例来说,如果您需要将node.scrollHeight设置为渲染的node.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 [...]

以我的经验window.requestAnimationFrame,还不足以确保DOM已从完全渲染/重排完整componentDidMount我运行的代码在componentDidMount调用后立即访问DOM,并且仅使用它window.requestAnimationFrame会导致DOM中存在该元素;但是,由于尚未发生回流,因此尚未反映出元素尺寸的更新。

唯一真正可靠的方法是将我的方法包装在setTimeout和中,window.requestAnimationFrame以确保在注册下一帧的渲染之前清除React的当前调用堆栈。

function onNextFrame(callback) {
    setTimeout(function () {
        requestAnimationFrame(callback)
    })
}

如果我不得不猜测为什么会发生这种情况/有必要,我可以看到React批处理DOM更新,并且直到当前堆栈完成后才真正将更改应用于DOM。

最终,如果您在代码中使用DOM度量,则需要在React回调之后触发,您可能需要使用此方法。

只是使用新的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.
         ...
     }, [])
}

您可以更改状态,然后在setState回调中进行计算根据React文档,这“保证在应用更新后会触发”。

这应该componentDidMount在代码或其他地方(例如在调整大小事件处理程序上)完成,而不是在构造函数中完成。

这是一个很好的替代方法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>
        );
    }
}

我觉得这个解决方案很脏,但是我们开始:

componentDidMount() {
    this.componentDidUpdate()
}

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

现在,我将坐在这里等待投票否决。

在这种情况下,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

getInitialState,getDefaultProps,componentWillMount,componentDidMount

我遇到了同样的问题。

在大多数情况下,使用hack-ish是可行setTimeout(() => { }, 0)componentDidMount()

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

注意:findDOMNode是用于访问基础DOM节点的转义口。在大多数情况下,不建议使用此逃生阴影,因为它会穿透组件抽象。

(来源: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));
}

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

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个具有固定宽度的react组件主div的高度,然后指定auto的第三个组件主div的高度。因此,将根据第三个div的内容指定高度。

我实际上在处理类似行为时遇到了麻烦,我在具有其id属性的Component中渲染了一个视频元素,因此当RenderDOM.render()结束时,它加载了一个需要ID来查找占位符的插件,但未能找到它。

在componentDidMount()内部具有0ms的setTimeout修复了它:)

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

对我而言,没有任何结合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);
        }
    }());
}

实际上,有比使用请求animationframe或timeout更简单,更简洁的版本。Iam感到惊讶的是没有人提出来:vanilla-js onload处理程序。如果可以,请使用组件安装,否则,只需在jsx组件的onload处理程序上绑定一个函数。如果要让函数运行每个渲染,则在返回结果之前,还要执行它。代码如下所示:

runAfterRender = () => 
{
  const myElem = document.getElementById("myElem")
  if(myElem)
  {
    //do important stuff
  }
}

render()
{
  this.runAfterRender()
  return (
    <div
      onLoad = {this.runAfterRender}
    >
      //more stuff
    </div>
  )
}

}

ReactDOM.render()文档中:

如果提供了可选的回调,它将在呈现或更新组件后执行。

当我需要打印接收大量数据并在画布上绘画的react组件时,我遇到了奇怪的情况。我已经尝试了所有提到的方法,但其中一种方法对我来说都不可靠,在setTimeout中使用requestAnimationFrame可以在20%的时间内获得空画布,因此我做了以下工作:

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

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

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组件生命周期方法:组件生命周期

每个组件都有很多与componentDidMount例如类似的方法

  • componentWillUnmount() -组件即将从浏览器DOM中删除
本文地址:http://javascript.askforanswer.com/fanyingxuanranhoudaima.html
文章标签: ,  
版权声明:本文为原创文章,版权归 javascript 所有,欢迎分享本文,转载请保留出处!

文件下载

老薛主机终身7折优惠码boke112

上一篇:
下一篇:

评论已关闭!