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
.
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:
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>
)
}