I'm using Preact for the first time.

I simply created a new project with preact-cli and this default template: https://github.com/preactjs-templates/default.

In app.js I'm trying to use this code:

import { Router } from 'preact-router';

import Header from './header';
import Home from '../routes/home';
import Profile from '../routes/profile';

// I added this function
function sleep(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
}

const App = async () => { // I added "async" and the "{" in this line
  await sleep(3000) // I added this line

  return ( // I added this line
    <div id="app">
      <Header />
      <Router>
        <Home path="/" />
        <Profile path="/profile/" user="me" />
        <Profile path="/profile/:user" />
      </Router>
    </div>
  )
} // I added this line

export default App;

But unfortunately browser's gives me error:

Uncaught Error: Objects are not valid as a child. Encountered an object with the keys {}.

Why?

It works if I do not use async/await.

2

There are 2 answers

0
marvinhagemeister On BEST ANSWER

Disclaimer: I work on Preact.

Our debug addon (preact/debug) will print this error whenever an invalid object is passed as a child that doesn't match the expected return type of h/createElement, usually called vnode:

const invalidVNode = { foo: 123 };
<div>{invalidVNode}</div>

In your case your component function returns a Promise which is an object in JavaScript. When Preact renders that component the render function will NOT return a vnode, but a Promise instead. That's why the error occurs.

Which poses the question:

How to do async initialization?

Once triggered, the render process in Preact is always synchronous. A component that returns a Promise breaks that contract. The reason it is that way is because you usually want to show at least something, like a spinner, to the user, while the asynchronous initialization is happening. A real world scenario for that would be fetching data via the network for example.

import { useEffect } from "preact/hooks";

const App = () => {
  // useEffect Hook is perfect for any sort of initialization code.
  // The second parameter is for checking when the effect should re-run.
  // We only want to initialize once when the component is created so we
  // pass an empty array so that nothing will be dirty checked.
  useEffect(() => {
    doSometThingAsyncHere()
  }, []);

  return (
    <div id="app">
      <Header />
      <Router>
        <Home path="/" />
        <Profile path="/profile/" user="me" />
        <Profile path="/profile/:user" />
      </Router>
    </div>
  )
}
4
Mohammad Faisal On

Reactjs is a component library. At the core it has a function like

React.createElement(component, props, ...children)

Here the first parameter is the component that you want to render.

When you are putting await sleep(3000) the function is not returning any valid children/html object rather it is returning an empty object. that's why you are getting this error.