preact.render does not return component instance

Created on 11 Dec 2017  ·  6Comments  ·  Source: preactjs/preact

The return value doesn't appear to be documented for either ReactDOM.render or preact.render, but with ReactDOM an instance of the component can be returned.

Example code via StackOverflow comment:

var Hello = React.createClass({
  render: function() {
    return (this.state) ? <div>Hello {this.state.name}</div> : null;
  }
});

var component = ReactDOM.render(<Hello/>, document.getElementById('container'));
component.setState({name: "World"});
setTimeout(function(){
  component.setState({name: "StackOverFlow"});
}, 1000);

This does not work with Preact; the return value of preact.render appears to be the real DOM rather than the virtual one. Is this intentional?

[I'll note that using React in this way appears to be frowned upon, at least in that StackOverflow thread I found. But imo it could be handy to have outside access to components, especially with small/store-less apps.]

compat wontfix

Most helpful comment

ah - you should use a ref on the root. That will work in both libs:

let instance;  // holds our component instance
let ref = c => {
  // c is the component instance
  instance = c;
};
render(<Foo ref={ref} />, parent)

console.log(instance)

All 6 comments

This indeed seems like a strange pattern. Trying to control a component class' state property outside of its class isn't a good idea. That's what props are made for.

In the example given, one could easily wrap <Hello/> with a component which includes the setTimeout portion.

import { h, Component } from "preact";

function Hello(props) {
  return this.props.name ? <div>Hello {this.props.name}</div> : null;
}

// Completely encapsulates timeout and passes the name as prop to Hello
class Timer extends Component {
  state = { name: "World" };

  componentDidMount() {
    this.timer = setTimeout(() => this.setState({ name: "StackOverflow" });
  }

  componentWillUnmount() {
    if (this.timer) {
      clearTimeout(this.timer);
    }
  }

  render() {
    return <Hello name={this.state.name} />;
  }
}

FWIW, this is one of the things preact-compat handles. You can see the code it uses to do so here.

I'd like to mark this as "won't fix" since it's not in scope for Preact. Is that alright?

@natevw I'm happy to brainstorm a solution with you regarding compat, if you're looking to avoid bringing in preact-compat itself.

@marvinhagemeister My real need is a bit different. I'm writing a small plugin that uses Preact internally but exposes only a vanilla JS API to apps that integrate it. At the end of the day, I do need some way to mess with the Preact world from outside of it — that's the goal here.


Here's the options I've considered and/or settled on for now:

I think properly I might have a view model store, a hierarchy of Plugin interface components, and then a public api that messes with the store to drive the plugin UI. But since the state is relatively simple I simply track it in the Plugin component — most of it is internal, but there's a few methods of that component that I want to expose to my public API "friend class" that it can then expose externally.

I considered having my Plugin component store a reference to itself on window in its constructor or componentWillMount but that seemed a little icky. Preact does store a reference to the component instance on the DOM element, so I settled on var api = preact.render(…)._theSecretInternalPropertyPreactUsesToStoreMyComponentInstance.myApiFactoryMethod().

Perhaps a more proper alternative would be to design the API something like this, i.e. re-rendering the root component every time the outside/exposed state needs to change?

class PublicAPI {
  constructor(el) {
    this._container = el
    this._propsForPlugin = {/* defaults */}
    this._render()
  }
  _render() {
    preact._render(h(Plugin, this._propsForPlugin), this._container)
  }

  doSomething() {
      this._propsForPlugin.something = true;
      this._render();
 } 
}

Seems the Preact API supports this, but I've also seen people frowning upon ever re-rendering the root component (in a React context anyway) and I somewhat agree. This does seem ickier than just having access to the instantiated component — e.g. if it were a native DOM element I wouldn't "recreate" it just to change one attribute.

ah - you should use a ref on the root. That will work in both libs:

let instance;  // holds our component instance
let ref = c => {
  // c is the component instance
  instance = c;
};
render(<Foo ref={ref} />, parent)

console.log(instance)

@developit D'oh! I had checked preact-compat for this, and even found that method, but somehow completely missed the obvious && out._component part that must have been right under my nose 😦

+1 for wontfixing this — I'm content doing my own ._component lookup even if it's a small maintenance concern should preact ever change its internals there ☝︎☝︎☝︎ ref thing above 💯 .

Was this page helpful?
0 / 5 - 0 ratings