Подтвердить что ты не робот

React - получение компонента из элемента DOM для отладки

В целях отладки в консоли существует ли какой-либо механизм, доступный в React, для использования экземпляра элемента DOM для получения компонента Backing React?

Этот вопрос задавался ранее в контексте использования его в производственном коде. Тем не менее, я сосредоточен на разработке сборок с целью отладки.

Я знаком с расширением отладки Chrome для React, однако это не доступно во всех браузерах. Комбинируя DOM-проводник и консоль, легко использовать ярлык "$ 0" для доступа к информации о выделенном элементе DOM.

Я хотел бы написать код примерно так в консоли отладки: getComponentFromElement ($ 0).props

Даже в сборке разработки React нет никакого механизма для использования, возможно, элемент ReactId для доступа к компоненту?

4b9b3361

Ответ 1

Я только что прочитал документы, и afaik ни один из внешних API API не позволит вам напрямую войти и найти компонент React по идентификатору. Однако вы можете обновить свой начальный вызов React.render() и сохранить возвращаемое значение где-нибудь, например:

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

Затем вы можете ссылаться на searchRoot и просматривать это напрямую или перемещаться по нему с помощью React.addons.TestUtils. например это даст вам все компоненты:

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

Существует несколько встроенных методов фильтрации этого дерева, или вы можете написать свою собственную функцию, чтобы возвращать компоненты только на основании какой-либо проверки, которую вы пишете.

Подробнее о TestUtils здесь: https://facebook.github.io/react/docs/test-utils.html

Ответ 2

Вот что я использую: (обновлено для работы с React <16 и 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;
    }
}

А потом использовать это:

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

Ответ 3

Ну вот. Это поддерживает 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;
};

Ответ 4

Я написал этот небольшой взлом, чтобы разрешить доступ к любому реагирующему компоненту из его dom node

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

то вы можете получить доступ к любому компоненту напрямую, используя:

document.getElementById("lol").react

или с помощью JQuery

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

Ответ 5

Вот небольшой фрагмент, который я использую в настоящее время.

Он работает с React 0.14.7.

Gist с кодом

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

Ответ 6

Реагировать 16+ версия:

Если вам нужен ближайший экземпляр компонента React, к которому принадлежит выбранный элемент DOM, вот как вы можете его найти (модифицировано из решения @Guan-Gui):

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

Их хитрость заключается в том, чтобы использовать свойство _debugOwner, которое является ссылкой на FiberNode ближайшего компонента, частью которого является элемент DOM.

Предупреждение: компоненты, работающие только в режиме разработки, будут иметь свойство _debugOwner. Это не будет работать в производственном режиме.

бонус

Я создал этот удобный фрагмент, который вы можете запустить в своей консоли, чтобы вы могли щелкнуть по любому элементу и получить экземпляр компонента React, к которому он принадлежит.

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

Ответ 7

Установите React devtools и используйте следующее для доступа к элементу реакции соответствующего узла dom ($ 0).

для 0.14.8

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

Конечно, это взломать только..

Ответ 8

Я адаптировал ответ @Venryx со слегка адаптированной версией ES6, которая соответствует моим потребностям. Эта вспомогательная функция возвращает текущий элемент вместо свойства _owner._instance.

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

Ответ 9

v15 и v16 совместимы с svg, html, комментариями, текстовыми узлами

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