482 Stimmen

React "nach dem Rendern" Code?

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?

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 ActionBar and BalanceBar, but how do I know when everything is rendered and where would I put the calculation stuff in this React Class?

/** @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 (

    );
  }
});

module.exports = AppBase;

351voto

Harborhoffer Punkte 5536

componentDidMount()

Diese Methode wird einmal aufgerufen, nachdem Ihr Komponente gerendert wurde. Ihr Code würde also so aussehen.

var AppBase = React.createClass({
  componentDidMount: function() {
    var $this = $(ReactDOM.findDOMNode(this));
    // set el height and width etc.
  },

  render: function () {
    return (

    );
  }
});

281voto

Graham P Heath Punkte 6544

Ein Nachteil der Verwendung von componentDidUpdate oder componentDidMount ist, dass sie tatsächlich ausgeführt werden, bevor die DOM-Elemente fertig gezeichnet sind, aber nachdem sie von React an den DOM des Browsers übergeben wurden.

Angenommen, Sie müssten node.scrollHeight auf node.scrollTop des gerenderten Knotens setzen, dann reichen Reacts DOM-Elemente möglicherweise nicht aus. Sie müssen warten, bis die Elemente fertig gezeichnet sind, um ihre Höhe zu erhalten.

Lösung:

Verwenden Sie requestAnimationFrame, um sicherzustellen, dass Ihr Code nach dem Malen Ihres neu gerenderten Objekts ausgeführt wird

scrollElement: function() {
  // Speichern eines 'this' ref, and
  var _this = this;
  // auf einen Anstrich warten, bevor scrollHeight-abhängiger Code ausgeführt wird.
  window.requestAnimationFrame(function() {
    var node = _this.getDOMNode();
    if (node !== undefined) {
      node.scrollTop = node.scrollHeight;
    }
  });
},
componentDidMount: function() {
  this.scrollElement();
},
// und oder
componentDidUpdate: function() {
  this.scrollElement();
},
// und oder
render: function() {
  this.scrollElement()
  return [...]

124voto

Elliot Chong Punkte 1380

In meiner Erfahrung reichte window.requestAnimationFrame nicht aus, um sicherzustellen, dass der DOM vollständig gerendert / neu geladen wurde von componentDidMount. Ich habe Code laufen, der unmittelbar nach einem componentDidMount Aufruf auf den DOM zugreift, und die ausschließliche Verwendung von window.requestAnimationFrame würde dazu führen, dass das Element im DOM vorhanden ist; jedoch sind Updates zu den Abmessungen des Elements noch nicht reflektiert, da ein erneutes Laden noch nicht erfolgt ist.

Der einzige wirklich zuverlässige Weg, um dies zu erreichen, war, meine Methode in ein setTimeout und ein window.requestAnimationFrame zu packen, um sicherzustellen, dass der aktuelle Aufrufstapel von React geleert wird, bevor für das Rendern des nächsten Frames registriert wird.

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

Wenn ich spekulieren müsste, warum dies passiert / notwendig ist, könnte ich mir vorstellen, dass React DOM-Aktualisierungen bündelt und die Änderungen tatsächlich erst am DOM anwendet, nachdem der aktuelle Stapel abgeschlossen ist.

Letztendlich, wenn Sie DOM-Messungen im Code verwenden, den Sie nach den React-Rückrufen ausführen, möchten Sie wahrscheinlich diese Methode verwenden.

45voto

P Fuster Punkte 2069

Nur um diese Frage ein wenig mit den neuen Hook-Methoden zu aktualisieren, können Sie einfach den useEffect Hook verwenden:

import React, { useEffect } from 'react'

export default function App(props) {

     useEffect(() => {
         // Ihr Post-Layout-Code (oder 'Effekt') hier.
         ...
     },
     // Array von Variablen, die ein Update auslösen können, wenn sie sich ändern. Übergeben Sie ein
     // ein leeres Array, wenn Sie es nur einmal ausführen möchten, nachdem das Komponenten montiert wurde.
     [])
}

Außerdem, wenn Sie vor dem Layout-Anstrich ausführen möchten, verwenden Sie den useLayoutEffect Hook:

import React, { useLayoutEffect } from 'react'

export default function App(props) {

     useLayoutEffect(() => {
         // Ihr Pre-Layout-Code (oder 'Effekt') hier.
         ...
     }, [])
}

21voto

Joseph238 Punkte 1174

Sie können den Zustand ändern und dann Ihre Berechnungen im setState-Callback durchführen. Laut der React-Dokumentation ist dies "garantiert nachdem das Update angewendet wurde".

Dies sollte in componentDidMount oder an einer anderen Stelle im Code (z. B. in einem Event-Handler für die Größenänderung) erfolgen, anstatt im Konstruktor.

Dies ist eine gute Alternative zu window.requestAnimationFrame und hat nicht die Probleme, die einige Benutzer hier erwähnt haben (die Notwendigkeit, es mit setTimeout zu kombinieren oder mehrmals aufzurufen). Zum Beispiel:

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 (

                ...

                ...

        );
    }
}

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X