Debugging Preact Apps

Preact ships with a lot of tools to make debugging easier. They're packaged in a single import and can be included by importing preact/debug.

These include a bridge to the excellent React Developer Tools Extension for Chrome and Firefox. If you have them already installed you can try it out on this site. Just open the devtools and start inspecting how we built it.

we'll print a warning or an error whenever we detect something wrong like incorrect nesting in <table> elements.



Installation

The React Developer Tools can be installed in the extension store of your browser.

Once installed we need to import preact/debug somewhere to initialize the connection to the extension. Make sure that this import is the first import in your whole app.

preact-cli does include the preact/debug package automatically. You can safely skip the next step if you're using it!

Here is an example of how your main entry file to your application may look like.

// Must be the first import
import "preact/debug";
import { render } from 'preact';
import App from './components/App';

render(<App />, document.getElementById('root'));Run in REPL

Strip devtools from production

Most bundlers allow you strip out code when they detect that a branch inside an if-statement will never be hit. We can use this to only include preact/debug during development and save those precious bytes in a production build.

// Must be the first import
if (process.env.NODE_ENV==='development') {
  // Must use require here as import statements are only allowed
  // to exist at the top of a file.
  require("preact/debug");
}

import { render } from 'preact';
import App from './components/App';

render(<App />, document.getElementById('root'));Run in REPL

Make sure to set the NODE_ENV variable to the correct value in your build tool.

Debug Warnings and Errors

Sometimes you'll may get warnings or errors whenever Preact detects invalid code. These should all be fixed to ensure that your app works flawlessly.

undefined parent passed to render()

This means that the code is trying to render your app into nothing instead of a DOM node. It's the difference between:

// What Preact received
render(<App />, undefined);

// vs what it expected
render(<App />, actualDomNode);Run in REPL

The main reason this error occurs is that the DOM node isn't present when the render() function is called. Make sure it exists.

undefined component passed to createElement()

Preact will throw this error whenever you pass undefined instead of a component. The common cause for this one is mixing up default and named exports.

// app.js
export default function App() {
  return <div>Hello World</div>;
}

// index.js: Wrong, because `app.js` doesn't have a named export
import { App } from './app';
render(<App />, dom);Run in REPL

The same error will be thrown when it's the other way around. When you declare a named export and are trying to use it as a default export. One quick way to check this (in case your editor won't do it already), is to just log out the import:

// app.js
export function App() {
  return <div>Hello World</div>;
}

// index.js
import App from './app';

console.log(App);
// Logs: { default: [Function] } instead of the componentRun in REPL

Passed a JSX literal as JSX twice

Passing a JSX-Literal or Component into JSX again is invalid and will trigger this error.

const Foo = <div>foo</div>;
// Invalid: Foo already contains a JSX-Element
render(<Foo />, dom);Run in REPL

To fix this, we can just pass the variable directly:

const Foo = <div>foo</div>;
render(Foo, dom);

Improper nesting of table detected

HTML has a very clear directions on how tables should be structured. Deviating from that will lead to rendering errors that are very hard to debug. In Preact we'll detect this and print an error. To learn more about how tables should be structured we can highly recommend the mdn documentation

Invalid ref-property

When the ref property contains something unexpected we'll throw this error. This includes string-based refs that have been deprecated a while ago.

// valid
<div ref={e => {/* ... */)}} />

// valid
const ref = createRef();
<div ref={ref} />

// Invalid
<div ref="ref" />Run in REPL

Invalid event handler

Sometimes you'll may accidentally pass a wrong value to an event handler. They must always be a function or null if you want to remove it. All other types are invalid.

// valid
<div onClick={() => console.log("click")} />

// invalid
<div onClick={console.log("click")} />Run in REPL

Hook can only be invoked from render methods

This error occurs when you try to use a hook outside of a component. They are only supported inside a function component.

// Invalid, must be used inside a component
const [value, setValue] = useState(0);

// valid
function Foo() {
  const [value, setValue] = useState(0);
  return <button onClick={() => setValue(value + 1)}>{value}</div>;
}Run in REPL

Getting vnode.[property] is deprecated

With Preact X we did some breaking changes to our internal vnode shape.

Preact 8.xPreact 10.x
vnode.nodeNamevnode.type
vnode.attributesvnode.props
vnode.childrenvnode.props.children

Found children with the same key

One unique aspect about virtual-dom based libraries is that they have to detect when a children is moved around. However to know which child is which, we need to flag them somehow. This is only necessary when you're creating children dynamically.

// Both children will have the same key "A"
<div>
  {['A', 'A'].map(char => <p key={char}>{char}</p>)}
</div>Run in REPL

The correct way to do it is to give them unique keys. In most cases the data you're iterating over will have some form of id.

const persons = [
  { name: 'John', age: 22 },
  { name: 'Sarah', age: 24}
];

// Somewhere later in your component
<div>
  {persons.map(({ name, age }) => {
    return <p key={name}>{name}, Age: {age}</p>;
  })}
</div>Run in REPL

Built by a bunch of lovely people like @AlexGalays.