web-dev-qa-db-fra.com

React - Obtenir un composant d'un élément DOM pour le débogage

Aux fins de débogage dans la console, existe-t-il un mécanisme disponible dans React pour utiliser une instance d'élément DOM afin d'obtenir le composant React de sauvegarde?

Cette question a déjà été posée dans le contexte de son utilisation dans le code de production. Cependant, je me concentre sur les constructions de développement à des fins de débogage.

Je connais l’extension Chrome de débogage pour React , mais elle n’est pas disponible dans tous les navigateurs. En combinant l'explorateur DOM et la console, il est facile d'utiliser le raccourci '$ 0' pour accéder aux informations relatives à l'élément DOM mis en évidence.

J'aimerais écrire quelque chose comme ceci dans la console de débogage: GetComponentFromElement ($ 0) .props

Même dans la version de développement de React, n’existe-t-il aucun mécanisme permettant d’utiliser éventuellement le ReactId de l’élément pour obtenir le composant?

46
LodeRunner28

Je viens de lire la documentation et, autant que je sache, aucune des API exposées de manière externe ne vous permet d'entrer directement et de rechercher un composant React par ID. Cependant, vous pouvez mettre à jour votre appel React.render() initial et conserver la valeur de retour quelque part, par exemple:

window.searchRoot = React.render(React.createElement......

Vous pouvez ensuite référencer searchRoot et parcourir directement cette information ou la parcourir à l'aide du React.addons.TestUtils. par exemple. cela vous donnera tous les composants:

var componentsArray = React.addons.TestUtils.findAllInRenderedTree(window.searchRoot, function() { return true; });

Il existe plusieurs méthodes intégrées pour filtrer cette arborescence, ou vous pouvez écrire votre propre fonction pour renvoyer uniquement les composants en fonction du contrôle que vous avez écrit.

Plus d'informations sur TestUtils ici: https://facebook.github.io/react/docs/test-utils.html

15
Josh from Qaribou

Voici ce que j'utilise: (mis à jour pour fonctionner avec React <16 et 16+)

window.FindReact = function(dom) {
    let key = Object.keys(dom).find(key=>key.startsWith("__reactInternalInstance$"));
    let internalInstance = dom[key];
    if (internalInstance == null) return null;

    if (internalInstance.return) { // react 16+
        return internalInstance._debugOwner
            ? internalInstance._debugOwner.stateNode
            : internalInstance.return.stateNode;
    } else { // react <16
        return internalInstance._currentElement._owner._instance;
    }
}

Et puis l'utiliser:

var someElement = document.getElementById("someElement");
FindReact(someElement).setState({test1: test2});
84
Venryx

Voici. Cela prend en charge React 16+

window.findReactComponent = function(el) {
  for (const key in el) {
    if (key.startsWith('__reactInternalInstance$')) {
      const fiberNode = el[key];

      return fiberNode && fiberNode.return && fiberNode.return.stateNode;
    }
  }
  return null;
};

21
Guan Gui

j'ai écrit ce petit hack pour permettre l'accès à tout composant de réaction de son nœud dom

var ReactDOM = require('react-dom');
(function () {
    var _render = ReactDOM.render;
    ReactDOM.render = function () {
        return arguments[1].react = _render.apply(this, arguments);
    };
})();

vous pouvez alors accéder directement à n’importe quel composant en utilisant:

document.getElementById("lol").react

ou en utilisant JQuery

$("#lol").get(0).react
5

Voici un petit extrait que j'utilise actuellement.

Cela fonctionne avec React 0.14.7.

Gist avec le code

let searchRoot = ReactDom.render(ROOT, document.getElementById('main'));

var getComponent = (comp) => comp._renderedComponent ? getComponent(comp._renderedComponent) : comp;

var getComponentById = (id)=> {
  var comp = searchRoot._reactInternalInstance;
  var path = id.substr(1).split('.').map(a=> '.' + a);
  if (comp._rootNodeID !== path.shift()) throw 'Unknown root';
  while (path.length > 0) {
    comp = getComponent(comp)._renderedChildren[path.shift()];
  }
  return comp._instance;
};

window.$r = (node)=> getComponentById(node.getAttribute('data-reactid'))

pour l'exécuter, ouvrez Devtools, mettez en surbrillance un élément à examiner et entrez le type de console suivant: $r($0)

2
Nadav Leshem

Installez React devtools et utilisez l'option suivant pour accéder à l'élément de réaction du noeud dom correspondant ($ 0) 

pour 0.14.8

    var findReactNode = (node) =>Object.values(__REACT_DEVTOOLS_GLOBAL_HOOK__.helpers)[0]
.getReactElementFromNative(node)
._currentElement;
       findReactNode($0);

Bien sûr, c'est un hack seulement ..

2
Shishir Arora

Réagissez à la version 16+:

Si vous voulez que l'occurrence du composant React la plus proche à laquelle appartient l'élément DOM sélectionné, voici comment vous pouvez la trouver (modifiée à partir de @ Guan-Gui's solution):

window.getComponentFromElement = function(el) {
  for (const key in el) {
    if (key.startsWith('__reactInternalInstance$')) {
      const fiberNode = el[key];
      return fiberNode && fiberNode._debugOwner && fiberNode._debugOwner.stateNode;
    }
  }
  return null;
};

Leur astuce consiste ici à utiliser la propriété _debugOwner, qui est une référence à FiberNode du composant le plus proche dont fait partie l'élément DOM.

Caveat: les composants ne fonctionneront que dans le mode dev. Ils auront la propriété _debugOwner. Cela ne fonctionnerait pas en mode de production.

Prime

J'ai créé cet extrait pratique que vous pouvez exécuter dans votre console pour pouvoir cliquer sur n'importe quel élément et obtenir l'instance du composant React à laquelle il appartient.

document.addEventListener('click', function(event) {
  const el = event.target;
  for (const key in el) {
    if (key.startsWith('__reactInternalInstance$')) {
      const fiberNode = el[key];
      const component = fiberNode && fiberNode._debugOwner;
      if (component) {
        console.log(component.type.displayName || component.type.name);
        window.$r = component.stateNode;
      }
      return;
    }
  }
});
2
Yangshun Tay

J'ai adapté la réponse de @ Venryx avec une version ES6 légèrement adaptée à mes besoins. Cette fonction d'assistance renvoie l'élément en cours au lieu de la propriété _owner._instance.

getReactDomComponent(dom) {
  const internalInstance = dom[Object.keys(dom).find(key =>
    key.startsWith('__reactInternalInstance$'))];
  if (!internalInstance) return null;
  return internalInstance._currentElement;
}
1
Noah

v15 et v16 compatibles avec svg, html, comment, noeuds de texte

/* Node extends text, svg, html
 usage for node $0:
    $0.reactive // returns [node, parentNode, rootNode]
    $0.react.props // {any:'prop'}
    $0.react.setState(...) // update
 */
Object.defineProperties(Node.prototype, {
    _react: {writable:true, value:''}
    ,reactKey: {
        get: function(){
            let symbol = this._react;
            if(symbol){ return symbol; }
            // v15, v16 use a string as key, probably a real symbol in the future
            symbol = Object.keys(this).find(key => key.startsWith('__reactInternalInstance$'));
            return Node.prototype._react = symbol || '';
        }
    }
    // try to find the props/state/React-instance
    ,react: {
        get: function(){
            let react = this[ this.reactKey ] || null;
            let $0;
            if(react){
                $0 = react._currentElement;
                if($0){ // v15
                    if($0._owner){
                        return $0._owner._instance;
                    }else{
                        return $0;
                    };
                }
                $0 = react.return;
                if($0){ // v16
                    // develop mode only: return react._debugOwner.stateNode
                    // both develop and prod modes:
                    return $0.stateNode
                }
            }else if(this._reactRootContainer){
                // v16 _internalRoot === _internalRoot.current.stateNode
                return this._reactRootContainer._internalRoot;
            }
            return react;
        }
    }
    // make a list of self, ancestors that make up this branch of the tree
    ,reactive: {
        get: function(list=[]){
            let $0 = this;
            while($0 && !$0[ $0.reactKey ] && !$0._reactRootContainer ){
                $0 = $0.previousSibling;
            };
            if($0 && ($0[$0.reactKey] || $0._reactRootContainer)){
                list.Push($0);
            };
            $0 = this;
            while($0 = $0.parentNode){
                if($0[ $0.reactKey ] || $0._reactRootContainer){
                    list.Push($0);
                }
            };
            return list;
        }
    }
});
0
jimmont