Preact: Question: Rendering (inserting) before instead of appending

Created on 14 Aug 2016  路  5Comments  路  Source: preactjs/preact

Maybe I've missed something. If render is the proper way to generate code with Preact, I can't seem to figure out the proper way to generate and insert code out-of-order. What I mean: render appends.

For example:

render(<div>Post 1: Hello World</div>, document.body);
render(<div>Post 2: Blah Blah</div>, document.body);

Which outputs:

<div>Post 1: Hello World</div>
<div>Post 2: Blah Blah</div>

I want this instead:

<div>Post 2: Blah Blah</div>
<div>Post 1: Hello World</div>

Other than the obvious (switching the order), or inserting proxy elements that I target instead, what should I be doing in the 2nd line so it goes before the 1st element; Or specifically, so it inserts at the top of document.body instead of the bottom?

Thanks.

question

Most helpful comment

You can definitely re-render using render(), just you lose a lot of the benefits of component-based design. It's also mildly confusing because there is render() (import { render } from 'preact') and there is also a render() method on components.

Here's a little more fully-featured example continuing from above:

import { h, Component, render } from 'preact';

class App extends Component {
  state = {
    posts: []   // we'll keep all received posts in an array
  };

  // when App is mounted, start polling for updates
  componentDidMount() {
    this.getUpdates();
  }

  // fetches new posts, then calls itself (to poll)
  getUpdates = () => {
    fetch('/updates')
      .then( r => r.json() )
      .then( newPosts => {
        // grab existing posts:
        let { posts } = this.state;

        // append new ones:
        posts = posts.concat(newPosts);

        // save updated posts to state (this automatically re-renders):
        this.setState({ posts });

        // wait a sec before polling again:
        setTimeout(this.getUpdates, 1000);
      });
  }

  // render gets called whenever state changes.
  // The returned JSX gets intelligently diffed against the dom.
  render(props, state) {
    // we can reverse-sort posts as part of the UI rendering:
    let posts = state.posts.slice().reverse();
    return (
      <div>
        { posts.map( post => (
          <div>
            {post.title}: {post.body}
          </div>
        )) }
      </div>
    );
  }
}

// render and mount App. This kicks everything off.
render(<App />, document.body);

One thing worth noting: React/preact/etc actually simplify this kind of UI nicely, but it takes a little while to get used to the different way of doing things. Whereas with jquery/etc you might have to know "I want to insert new posts above existing posts", with this component model, you only have to tell it how to render _all_ posts (whether they are new or old). This way, determining whether new posts get injected first or last is simply a matter of choosing where to put them in an Array.

All 5 comments

@povrazor Hmm - It's fairly odd to invoke render() multiple times for a single use-case. Is there a reason you want two top-level renders?

Normally your example would be written as a component:

class Example extends Component {
  state = {
    posts: [
      { title: 'Post 1', body: 'Hello World' },
      { title: 'Post 2', body: 'Blah Blah' }
    ]
  };
  render(props, state) {
    // we can reverse-sort posts as part of the UI rendering:
    let posts = state.posts.slice().reverse();
    return (
      <div>
        { posts.map( post => (
          <div>
            {post.title}: {post.body}
          </div>
        )) }
      </div>
    );
  }
}

// finally, and generally only in one place per app, we initialize rendering:
render(<Example />, document.body);

edit: Sorry if I missed the point here, let me know if I answered the wrong question 馃槢

What I'm building is a bit twitter-like. It regularly polls a server for posts, and when there's something new, I need to insert the new posts at the top of the list.

I guess I missed the part where render is supposed to be a one-time call. :). Apparently I made a few too many assumptions about how I should be using Preact from the samples.

You can definitely re-render using render(), just you lose a lot of the benefits of component-based design. It's also mildly confusing because there is render() (import { render } from 'preact') and there is also a render() method on components.

Here's a little more fully-featured example continuing from above:

import { h, Component, render } from 'preact';

class App extends Component {
  state = {
    posts: []   // we'll keep all received posts in an array
  };

  // when App is mounted, start polling for updates
  componentDidMount() {
    this.getUpdates();
  }

  // fetches new posts, then calls itself (to poll)
  getUpdates = () => {
    fetch('/updates')
      .then( r => r.json() )
      .then( newPosts => {
        // grab existing posts:
        let { posts } = this.state;

        // append new ones:
        posts = posts.concat(newPosts);

        // save updated posts to state (this automatically re-renders):
        this.setState({ posts });

        // wait a sec before polling again:
        setTimeout(this.getUpdates, 1000);
      });
  }

  // render gets called whenever state changes.
  // The returned JSX gets intelligently diffed against the dom.
  render(props, state) {
    // we can reverse-sort posts as part of the UI rendering:
    let posts = state.posts.slice().reverse();
    return (
      <div>
        { posts.map( post => (
          <div>
            {post.title}: {post.body}
          </div>
        )) }
      </div>
    );
  }
}

// render and mount App. This kicks everything off.
render(<App />, document.body);

One thing worth noting: React/preact/etc actually simplify this kind of UI nicely, but it takes a little while to get used to the different way of doing things. Whereas with jquery/etc you might have to know "I want to insert new posts above existing posts", with this component model, you only have to tell it how to render _all_ posts (whether they are new or old). This way, determining whether new posts get injected first or last is simply a matter of choosing where to put them in an Array.

Awesome. Thank you so much for the example. I think state and setState was the other detail I misunderstood that tied this all together (the re-render).

Thanks again for your help!

Happy to help :) Hopefully I'll have some time to put better documentation together - I'd like to do some tutorials and such, perhaps on EggHead.io.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

simonjoom picture simonjoom  路  3Comments

youngwind picture youngwind  路  3Comments

paulkatich picture paulkatich  路  3Comments

matuscongrady picture matuscongrady  路  3Comments

adriaanwm picture adriaanwm  路  3Comments