From ddedca17149ed16d05a5d498cdaf6087345d1d9d Mon Sep 17 00:00:00 2001 From: jdecroock Date: Sat, 24 May 2025 11:29:29 +0200 Subject: [PATCH] Start v11 docs --- content/en/guide/v11/api-reference.md | 193 ++++++ content/en/guide/v11/components.md | 205 +++++++ content/en/guide/v11/context.md | 96 +++ content/en/guide/v11/debugging.md | 212 +++++++ content/en/guide/v11/differences-to-react.md | 221 +++++++ .../en/guide/v11/external-dom-mutations.md | 80 +++ content/en/guide/v11/forms.md | 177 ++++++ content/en/guide/v11/getting-started.md | 271 ++++++++ content/en/guide/v11/hooks.md | 462 ++++++++++++++ content/en/guide/v11/no-build-workflows.md | 123 ++++ content/en/guide/v11/options.md | 93 +++ .../en/guide/v11/preact-testing-library.md | 256 ++++++++ content/en/guide/v11/refs.md | 125 ++++ content/en/guide/v11/server-side-rendering.md | 135 ++++ content/en/guide/v11/signals.md | 553 +++++++++++++++++ content/en/guide/v11/switching-to-preact.md | 144 +++++ content/en/guide/v11/tutorial.md | 218 +++++++ content/en/guide/v11/typescript.md | 578 ++++++++++++++++++ .../en/guide/v11/unit-testing-with-enzyme.md | 225 +++++++ content/en/guide/v11/upgrade-guide.md | 5 + content/en/guide/v11/web-components.md | 190 ++++++ content/en/guide/v11/whats-new.md | 137 +++++ src/assets/_redirects | 16 +- src/config.json | 2 +- src/lib/page-title.js | 1 + 25 files changed, 4709 insertions(+), 9 deletions(-) create mode 100644 content/en/guide/v11/api-reference.md create mode 100644 content/en/guide/v11/components.md create mode 100644 content/en/guide/v11/context.md create mode 100644 content/en/guide/v11/debugging.md create mode 100644 content/en/guide/v11/differences-to-react.md create mode 100644 content/en/guide/v11/external-dom-mutations.md create mode 100644 content/en/guide/v11/forms.md create mode 100644 content/en/guide/v11/getting-started.md create mode 100644 content/en/guide/v11/hooks.md create mode 100644 content/en/guide/v11/no-build-workflows.md create mode 100644 content/en/guide/v11/options.md create mode 100644 content/en/guide/v11/preact-testing-library.md create mode 100644 content/en/guide/v11/refs.md create mode 100644 content/en/guide/v11/server-side-rendering.md create mode 100644 content/en/guide/v11/signals.md create mode 100644 content/en/guide/v11/switching-to-preact.md create mode 100644 content/en/guide/v11/tutorial.md create mode 100644 content/en/guide/v11/typescript.md create mode 100644 content/en/guide/v11/unit-testing-with-enzyme.md create mode 100644 content/en/guide/v11/upgrade-guide.md create mode 100644 content/en/guide/v11/web-components.md create mode 100644 content/en/guide/v11/whats-new.md diff --git a/content/en/guide/v11/api-reference.md b/content/en/guide/v11/api-reference.md new file mode 100644 index 000000000..734c19a92 --- /dev/null +++ b/content/en/guide/v11/api-reference.md @@ -0,0 +1,193 @@ +--- +name: API Reference +description: 'Learn more about all exported functions of the Preact module' +--- + +# API Reference + +This page serves as a quick overview over all exported functions. + +--- + +
+ +--- + +## Component + +`Component` is a base class that can be extended to create stateful Preact components. + +Rather than being instantiated directly, Components are managed by the renderer and created as-needed. + +```js +import { Component } from 'preact'; + +class MyComponent extends Component { + // (see below) +} +``` + +### Component.render(props, state) + +All components must provide a `render()` function. The render function is passed the component's current props and state, and should return a Virtual DOM Element (typically a JSX "element"), an Array, or `null`. + +```jsx +import { Component } from 'preact'; + +class MyComponent extends Component { + render(props, state) { + // props is the same as this.props + // state is the same as this.state + + return

Hello, {props.name}!

; + } +} +``` + +To learn more about components and how they can be used, check out the [Components Documentation](/guide/v11/components). + +## render() + +`render(virtualDom, containerNode)` + +Render a Virtual DOM Element into a parent DOM element `containerNode`. Does not return anything. + +```jsx +// --repl +// DOM tree before render: +//
+ +import { render } from 'preact'; + +const Foo = () =>
foo
; + +render(, document.getElementById('container')); + +// After render: +//
+//
foo
+//
+``` + +## hydrate() + +If you've already pre-rendered or server-side-rendered your application to HTML, Preact can bypass most rendering work when loading in the browser. This can be enabled by switching from `render()` to `hydrate()`, which skips most diffing while still attaching event listeners and setting up your component tree. This works only when used in conjunction with pre-rendering or [Server-Side Rendering](/guide/v11/server-side-rendering). + +```jsx +// --repl +import { hydrate } from 'preact'; + +const Foo = () =>
foo
; +hydrate(, document.getElementById('container')); +``` + +## h() / createElement() + +`h(type, props, ...children)` + +Returns a Virtual DOM Element with the given `props`. Virtual DOM Elements are lightweight descriptions of a node in your application's UI hierarchy, essentially an object of the form `{ type, props }`. + +After `type` and `props`, any remaining parameters are collected into a `children` property. +Children may be any of the following: + +- Scalar values (string, number, boolean, null, undefined, etc) +- Nested Virtual DOM Elements +- Infinitely nested Arrays of the above + +```js +import { h } from 'preact'; + +h('div', { id: 'foo' }, 'Hello!'); +//
Hello!
+ +h('div', { id: 'foo' }, 'Hello', null, ['Preact!']); +//
Hello Preact!
+ +h( + 'div', + { id: 'foo' }, + h('span', null, 'Hello!') +); +//
Hello!
+``` + +## toChildArray + +This helper function converts a `props.children` value to a flattened Array regardless of its structure or nesting. If `props.children` is already an array, a copy is returned. This function is useful in cases where `props.children` may not be an array, which can happen with certain combinations of static and dynamic expressions in JSX. + +For Virtual DOM Elements with a single child, `props.children` is a reference to the child. When there are multiple children, `props.children` is always an Array. The `toChildArray` helper provides a way to consistently handle all cases. + +```jsx +import { toChildArray } from 'preact'; + +function Foo(props) { + const count = toChildArray(props.children).length; + return
I have {count} children
; +} + +// props.children is "bar" +render( + bar, + container +); + +// props.children is [

A

,

B

] +render( + +

A

+

B

+
, + container +); +``` + +## cloneElement + +`cloneElement(virtualElement, props, ...children)` + +This function allows you to create a shallow copy of a Virtual DOM Element. +It's generally used to add or overwrite `props` of an element: + +```jsx +function Linkout(props) { + // add target="_blank" to the link: + return cloneElement(props.children, { target: '_blank' }); +} +render(home); +// home +``` + +## createContext + +See the section in the [Context documentation](/guide/v11/context#createcontext). + +## createRef + +Provides a way to reference an element or component once it has been rendered. + +See the [References documentation](/guide/v11/refs#createref) for more details. + +## Fragment + +A special kind of component that can have children, but is not rendered as a DOM element. +Fragments make it possible to return multiple sibling children without needing to wrap them in a DOM container: + +```jsx +// --repl +import { Fragment, render } from 'preact'; + +render( + +
A
+
B
+
C
+
, + document.getElementById('container') +); +// Renders: +//
; + +// Renders:
My name is John Doe.
+render(App, document.body); +``` + +> Note in earlier versions they were known as `"Stateless Components"`. This doesn't hold true anymore with the [hooks-addon](/guide/v11/hooks). + +## Class Components + +Class components can have state and lifecycle methods. The latter are special methods, that will be called when a component is attached to the DOM or destroyed for example. + +Here we have a simple class component called `` that displays the current time: + +```jsx +// --repl +import { Component, render } from 'preact'; + +// --repl-before +class Clock extends Component { + + constructor() { + super(); + this.state = { time: Date.now() }; + } + + // Lifecycle: Called whenever our component is created + componentDidMount() { + // update time every second + this.timer = setInterval(() => { + this.setState({ time: Date.now() }); + }, 1000); + } + + // Lifecycle: Called just before our component will be destroyed + componentWillUnmount() { + // stop when not renderable + clearInterval(this.timer); + } + + render() { + let time = new Date(this.state.time).toLocaleTimeString(); + return {time}; + } +} +// --repl-after +render(, document.getElementById('app')); +``` + +### Lifecycle Methods + +In order to have the clock's time update every second, we need to know when `` gets mounted to the DOM. _If you've used HTML5 Custom Elements, this is similar to the `attachedCallback` and `detachedCallback` lifecycle methods._ Preact invokes the following lifecycle methods if they are defined for a Component: + +| Lifecycle method | When it gets called | +|-----------------------------|--------------------------------------------------| +| `componentDidMount()` | after the component gets mounted to the DOM +| `componentWillUnmount()` | prior to removal from the DOM +| `getDerivedStateFromProps(nextProps, prevState)` | just before `shouldComponentUpdate`. Return object to update state or `null` to skip update. Use with care. +| `shouldComponentUpdate(nextProps, nextState, nextContext)` | before `render()`. Return `false` to skip render +| `getSnapshotBeforeUpdate(prevProps, prevState)` | called just before `render()`. return value is passed to `componentDidUpdate`. +| `componentDidUpdate(prevProps, prevState, snapshot)` | after `render()` + +Here's a visual overview of how they relate to each other (originally posted in [a tweet](https://web.archive.org/web/20191118010106/https://twitter.com/dan_abramov/status/981712092611989509) by Dan Abramov): + +![Diagram of component lifecycle methods](/guide/components-lifecycle-diagram.png) + +### Error Boundaries + +An error boundary is a component that implements either `componentDidCatch()` or the static method `getDerivedStateFromError()` (or both). These are special methods that allow you to catch any errors that happen during rendering and are typically used to provide nicer error messages or other fallback content and save information for logging purposes. It's important to note that error boundaries cannot catch all errors and those thrown in event handlers or asynchronous code (like a `fetch()` call) need to be handled separately. + +When an error is caught, we can use these methods to react to any errors and display a nice error message or any other fallback content. + +```jsx +// --repl +import { Component, render } from 'preact'; +// --repl-before +class ErrorBoundary extends Component { + constructor() { + super(); + this.state = { errored: false }; + } + + static getDerivedStateFromError(error) { + return { errored: true }; + } + + componentDidCatch(error, errorInfo) { + errorReportingService(error, errorInfo); + } + + render(props, state) { + if (state.errored) { + return

Something went badly wrong

; + } + return props.children; + } +} +// --repl-after +render(, document.getElementById('app')); +``` + +## Fragments + +A `Fragment` allows you to return multiple elements at once. They solve the limitation of JSX where every "block" must have a single root element. You'll often encounter them in combination with lists, tables or with CSS flexbox where any intermediate element would otherwise affect styling. + +```jsx +// --repl +import { Fragment, render } from 'preact'; + +function TodoItems() { + return ( + +
  • A
  • +
  • B
  • +
  • C
  • +
    + ) +} + +const App = ( +
      + +
    • D
    • +
    +); + +render(App, container); +// Renders: +//
      +//
    • A
    • +//
    • B
    • +//
    • C
    • +//
    • D
    • +//
    +``` + +Note that most modern transpilers allow you to use a shorter syntax for `Fragments`. The shorter one is a lot more common and is the one you'll typically encounter. + +```jsx +// This: +const Foo = foo; +// ...is the same as this: +const Bar = <>foo; +``` + +You can also return arrays from your components: + +```jsx +function Columns() { + return [ + Hello, + World + ]; +} +``` + +Don't forget to add keys to `Fragments` if you create them in a loop: + +```jsx +function Glossary(props) { + return ( +
    + {props.items.map(item => ( + // Without a key, Preact has to guess which elements have + // changed when re-rendering. + +
    {item.term}
    +
    {item.description}
    +
    + ))} +
    + ); +} +``` diff --git a/content/en/guide/v11/context.md b/content/en/guide/v11/context.md new file mode 100644 index 000000000..677113df0 --- /dev/null +++ b/content/en/guide/v11/context.md @@ -0,0 +1,96 @@ +--- +name: Context +description: 'Context allows you to pass props through intermediate components. This documents describes both the new and the old API' +--- + +# Context + +Context allows you to pass a value to a child deep down the tree without having to pass it through every component in-between via props. A very popular use case for this is theming. In a nutshell context can be thought of a way to do pub-sub-style updates in Preact. + +There are two different ways to use context: Via the newer `createContext` API and the legacy context API. The difference between the two is that the legacy one can't update a child when a component inbetween aborts rendering via `shouldComponentUpdate`. That's why we highly recommend to always use `createContext`. + +--- + +
    + +--- + +## createContext + +First we need to create a context object we can pass around. This is done via the `createContext(initialValue)` function. It returns a `Provider` component that is used to set the context value and a `Consumer` one which retrieves the value from the context. + +The `initialValue` argument is only used when a context does not have a matching `Provider` above it in the tree. This may be helpful for testing components in isolation, as it avoids the need for creating a wrapping `Provider`. + +```jsx +// --repl +import { render, createContext } from 'preact'; + +const SomeComponent = props => props.children; +// --repl-before +const Theme = createContext('light'); + +function ThemedButton(props) { + return ( + + {theme => { + return ; + }} + + ); +} + +function App() { + return ( + + + + + + ); +} +// --repl-after +render(, document.getElementById("app")); +``` + +> An easier way to use context is via the [useContext](/guide/v11/hooks#usecontext) hook. + +## Legacy Context API + +We include the legacy API mainly for backwards-compatibility reasons. It has been superseded by the `createContext` API. The legacy API has known issues like blocking updates if there are components in-between that return `false` in `shouldComponentUpdate`. If you nonetheless need to use it, keep reading. + +To pass down a custom variable through the context, a component needs to have the `getChildContext` method. There you return the new values you want to store in the context. The context can be accessed via the second argument in function components or `this.context` in a class-based component. + +```jsx +// --repl +import { render } from 'preact'; + +const SomeOtherComponent = props => props.children; +// --repl-before +function ThemedButton(props, context) { + return ( + + ); +} + +class App extends Component { + getChildContext() { + return { + theme: 'light' + } + } + + render() { + return ( +
    + + + +
    + ); + } +} +// --repl-after +render(, document.getElementById("app")); +``` diff --git a/content/en/guide/v11/debugging.md b/content/en/guide/v11/debugging.md new file mode 100644 index 000000000..6a8bfa292 --- /dev/null +++ b/content/en/guide/v11/debugging.md @@ -0,0 +1,212 @@ +--- +name: Debugging Preact Apps +description: 'How to debug Preact applications when something goes wrong.' +--- + +# 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 integration with our own [Preact Devtools] Extension for Chrome and Firefox. + +We'll print a warning or an error whenever we detect something wrong like incorrect nesting in `` elements. + +--- + +
    + +--- + +## Installation + +The [Preact Devtools] can be installed in the extension store of your browser. + +- [For Chrome](https://chrome.google.com/webstore/detail/preact-developer-tools/ilcajpmogmhpliinlbcdebhbcanbghmd) +- [For Firefox](https://addons.mozilla.org/en-US/firefox/addon/preact-devtools/) +- [For Edge](https://microsoftedge.microsoft.com/addons/detail/hdkhobcafnfejjieimdkmjaiihkjpmhk) + +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/preset-vite` includes the `preact/debug` package automatically. You can safely skip the setup & strip steps if you're using it! + +Here is an example of how your main entry file to your application may look like. + +```jsx +// Must be the first import +import "preact/debug"; +import { render } from 'preact'; +import App from './components/App'; + +render(, document.getElementById('root')); +``` + +### 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. + +```jsx +// Must be the first import +if (process.env.NODE_ENV==='development') { + // Must use require here as import statements are only allowed + // to exist at top-level. + require("preact/debug"); +} + +import { render } from 'preact'; +import App from './components/App'; + +render(, document.getElementById('root')); +``` + +Make sure to set the `NODE_ENV` variable to the correct value in your build tool. + +## Debug Warnings and Errors + +Sometimes you may get warnings or errors when Preact detects invalid code. These should 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: + +```jsx +// What Preact received +render(, undefined); + +// vs what it expected +render(, actualDomNode); +``` + +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. + +```jsx +// app.js +export default function App() { + return
    Hello World
    ; +} + +// index.js: Wrong, because `app.js` doesn't have a named export +import { App } from './app'; +render(, dom); +``` + +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: + +```jsx +// app.js +export function App() { + return
    Hello World
    ; +} + +// index.js +import App from './app'; + +console.log(App); +// Logs: { default: [Function] } instead of the component +``` + +### Passed a JSX literal as JSX twice + +Passing a JSX-Literal or Component into JSX again is invalid and will trigger this error. + +```jsx +const Foo =
    foo
    ; +// Invalid: Foo already contains a JSX-Element +render(, dom); +``` + +To fix this, we can just pass the variable directly: + +```jsx +const Foo =
    foo
    ; +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](https://developer.mozilla.org/en-US/docs/Learn/HTML/Tables/Basics) + +### 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. + +```jsx +// valid +
    {/* ... */)}} /> + +// valid +const ref = createRef(); +
    + +// Invalid +
    +``` + +### 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. + +```jsx +// valid +
    console.log("click")} /> + +// invalid +
    +``` + +### 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. + +```jsx +// Invalid, must be used inside a component +const [value, setValue] = useState(0); + +// valid +function Foo() { + const [value, setValue] = useState(0); + return ; +} +``` + +### Getting `vnode.[property]` is deprecated + +With Preact X we did some breaking changes to our internal `vnode` shape. + +| Preact 8.x | Preact 10.x/11.x | +| ------------------ | ---------------------- | +| `vnode.nodeName` | `vnode.type` | +| `vnode.attributes` | `vnode.props` | +| `vnode.children` | `vnode.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._ + +```jsx +// Both children will have the same key "A" +
    + {['A', 'A'].map(char =>

    {char}

    )} +
    +``` + +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`. + +```jsx +const persons = [ + { name: 'John', age: 22 }, + { name: 'Sarah', age: 24} +]; + +// Somewhere later in your component +
    + {persons.map(({ name, age }) => { + return

    {name}, Age: {age}

    ; + })} +
    +``` + +[Preact Devtools]: https://preactjs.github.io/preact-devtools/ diff --git a/content/en/guide/v11/differences-to-react.md b/content/en/guide/v11/differences-to-react.md new file mode 100644 index 000000000..a3cff9d08 --- /dev/null +++ b/content/en/guide/v11/differences-to-react.md @@ -0,0 +1,221 @@ +--- +name: Differences to React +permalink: '/guide/differences-to-react' +description: 'What are the differences between Preact and React. This document describes them in detail' +--- + +# Differences to React + +Preact is not intended to be a reimplementation of React. There are differences. Many of these differences are trivial, or can be completely removed by using [preact/compat], which is a thin layer over Preact that attempts to achieve 100% compatibility with React. + +The reason Preact does not attempt to include every single feature of React is in order to remain **small** and **focused** - otherwise it would make more sense to simply submit optimizations to the React project, which is already a very complex and well-architected codebase. + +--- + +
    + +--- + +## Main differences + +The main difference between Preact and React is that Preact does not implement a synthetic event system for size and performance reasons. Preact uses the browser's standard `addEventListener` to register event handlers, which means event naming and behavior works the same in Preact as it does in plain JavaScript / DOM. See [MDN's Event Reference] for a full list of DOM event handlers. + +Standard browser events work very similarly to how events work in React, with a few small differences. In Preact: + +- events don't bubble up through `` components +- standard `onInput` should be used instead of React's `onChange` for form inputs (**only if `preact/compat` is not used**) +- standard `onDblClick` should be used instead of React's `onDoubleClick` (**only if `preact/compat` is not used**) +- `onSearch` should generally be used for ``, since the clear "x" button does not fire `onInput` in IE11 + +Another notable difference is that Preact follows the DOM specification more closely. Custom elements are supported like any other element, and custom events are supported with case-sensitive names (as they are in the DOM). + +## Version Compatibility + +For both preact and [preact/compat], version compatibility is measured against the _current_ and _previous_ major releases of React. When new features are announced by the React team, they may be added to Preact's core if it makes sense given the [Project Goals]. This is a fairly democratic process, constantly evolving through discussion and decisions made in the open, using issues and pull requests. + +> Thus, the website and documentation reflect React `15.x` through `17.x` when discussing compatibility or making comparisons. + +## Debug messages and errors + +Our flexible architecture allows addons to enhance the Preact experience in any way they want. One of those addons is `preact/debug` which adds [helpful warnings and errors](/guide/v11/debugging) and attaches the [Preact Developer Tools](https://preactjs.github.io/preact-devtools/) browser extension, if installed. Those guide you when developing Preact applications and make it a lot easier to inspect what's going on. You can enable them by adding the relevant import statement: + +```js +import "preact/debug"; // <-- Add this line at the top of your main entry file +``` + +This is different from React which requires a bundler being present that strips out debugging messages at build time by checking for `NODE_ENV != "production"`. + +## Features unique to Preact + +Preact actually adds a few convenient features inspired by work in the (P)React community: + +### Native support for ES Modules + +Preact was built with ES Modules in mind from the beginning, and was one of the first frameworks to support them. You can load Preact via the `import` keyword directly in browsers without having it to pass through a bundler first. + +### Arguments in `Component.render()` + +For convenience, we pass `this.props` and `this.state` to the `render()` method on class components. Take a look at this component which uses one prop and one state property. + +```jsx +// Works in both Preact and React +class Foo extends Component { + state = { age: 1 }; + + render() { + return
    Name: {this.props.name}, Age: {this.state.age}
    ; + } +} +``` + +In Preact this can be also written like this: + +```jsx +// Only works in Preact +class Foo extends Component { + state = { age: 1 }; + + render({ name }, { age }) { + return
    Name: {name}, Age: {age}
    ; + } +} +``` + +Both snippets render the exact same thing, render arguments are provided for convenience. + +### Raw HTML attribute/property names + +Preact aims to closely match the DOM specification supported by all major browsers. When applying `props` to an element, Preact _detects_ whether each prop should be set as a property or HTML attribute. This makes it possible to set complex properties on Custom Elements, but it also means you can use attribute names like `class` in JSX: + +```jsx +// This: +
    + +// ...is the same as: +
    +``` + +Most Preact developers prefer to use `class` because it's shorter to write, but both are supported. + +### SVG inside JSX + +SVG is pretty interesting when it comes to the names of its properties and attributes. Some properties (and their attributes) on SVG objects are camelCased (e.g. [clipPathUnits on a clipPath element](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/clipPath#Attributes)), some attributes are kebab-case (e.g. [clip-path on many SVG elements](https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/Presentation)), and other attributes (usually ones inherited from the DOM, e.g. `oninput`) are all lowercase. + +Preact applies SVG attributes as-written. This means you can copy and paste unmodified SVG snippets right into your code and have them work out of the box. This allows greater interoperability with tools designers tend to use to generate icons or SVG illustrations. + +```jsx +// React + + + +// Preact (note stroke-width and stroke-linejoin) + + + +``` + +If you're coming from React, you may be used to specifying all attributes in camelCase. You can continue to use always-camelCase SVG attribute names by adding [preact/compat] to your project, which mirrors the React API and normalizes these attributes. + +### Use `onInput` instead of `onChange` + +Largely for historical reasons, the semantics of React's `onChange` event are actually the same as the `onInput` event provided by browsers, which is supported everywhere. The `input` event is the best-suited event for the majority of cases where you want to react when a form control is modified. In Preact core, `onChange` is the standard [DOM change event](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/change_event) that gets fired when an element's value is _committed_ by the user. + +```jsx +// React + console.log(e.currentTarget.value)} /> + +// Preact + console.log(e.currentTarget.value)} /> +``` + +If you're using [preact/compat], most `onChange` events are internally converted to `onInput` to emulate React's behavior. This is one of the tricks we use to ensure maximum compatibility with the React ecosystem. + +### JSX Constructor + +JSX is a syntax extension for JavaScript that is converted to nested function calls. The idea of using these nested calls to build up tree structures long predates JSX, and was previously popularized in JavaScript by the [hyperscript] project. This approach has value well beyond the scope of the React ecosystem, so Preact promotes the original generalized community-standard. For a more in-depth discussion of JSX and its relationship to Hyperscript, [read this article on how JSX works](https://jasonformat.com/wtf-is-jsx). + +**Source:** (JSX) + +```jsx + + Home + +``` + +**Output:** + +```js +// Preact: +h( + 'a', + { href:'/' }, + h('span', null, 'Home') +); + +// React: +React.createElement( + 'a', + { href:'/' }, + React.createElement('span', null, 'Home') +); +``` + +Ultimately, if you're looking at the generated output code for a Preact application, it's clear that a shorter un-namespaced "JSX pragma" is both easier to read _and_ more suitable for optimizations like minification. In most Preact apps you'll encounter `h()`, though it doesn't really matter which name you use since a `createElement` alias export is also provided. + +### No contextTypes needed + +The legacy `Context` API requires Components to declare specific properties using React's `contextTypes` or `childContextTypes` in order to receive those values. Preact does not have this requirement: all Components receive all `context` properties produced by `getChildContext()` by default. + +## Features exclusive to `preact/compat` + +`preact/compat` is our **compat**ibility layer that translates React code to Preact. For existing React users this can be an easy way to try out Preact without changing any of your code, by [setting up a few aliases](/guide/v11/getting-started#aliasing-react-to-preact) in your bundler configuration. + +### Children API + +The `Children` API is a specialized set of methods for working with the value of `props.children`. For Preact this is generally unnecessary, and we recommend using the built-in array methods instead. In Preact, `props.children` is either a Virtual DOM node, an empty value like `null`, or an Array of Virtual DOM nodes. The first two cases are the simplest and most common, since it's possible to use or return `children` as-is: + +```jsx +// React: +function App(props) { + return +} + +// Preact: use props.children directly: +function App(props) { + return +} +``` + +For specialized cases where you need to iterate over the children passed to a component, Preact provides a `toChildArray()` method that accepts any `props.children` value and returns a flattened and normalized Array of Virtual DOM nodes. + +```jsx +// React +function App(props) { + const cols = Children.count(props.children); + return
    {props.children}
    +} + +// Preact +function App(props) { + const cols = toChildArray(props.children).length; + return
    {props.children}
    +} +``` + +A React-compatible `Children` API is available from `preact/compat` to make integration with existing component libraries seamless. + +### Specialised Components + +[preact/compat] ships with specialised components that are not necessary for every app. These include + +- [PureComponent](/guide/v11/switching-to-preact#purecomponent): Only updates if `props` or `state` have changed +- [memo](/guide/v11/switching-to-preact#memo): Similar in spirit to `PureComponent` but allows to use a custom comparison function +- [forwardRef](/guide/v11/switching-to-preact#forwardref): Supply a `ref` to a specified child component. +- [Portals](/guide/v11/switching-to-preact#portals): Continues rendering the current tree into a different DOM container +- [Suspense](/guide/v11/switching-to-preact#suspense-experimental): **experimental** Allows to display fallback content in case the tree is not ready +- [lazy](/guide/v11/switching-to-preact#suspense-experimental): **experimental** Lazy load async code and mark a tree as ready/not ready accordingly. + +[Project Goals]: /about/project-goals +[hyperscript]: https://github.com/dominictarr/hyperscript +[preact/compat]: /guide/v11/switching-to-preact +[MDN's Event Reference]: https://developer.mozilla.org/en-US/docs/Web/Events diff --git a/content/en/guide/v11/external-dom-mutations.md b/content/en/guide/v11/external-dom-mutations.md new file mode 100644 index 000000000..cfd6ce99a --- /dev/null +++ b/content/en/guide/v11/external-dom-mutations.md @@ -0,0 +1,80 @@ +--- +name: External DOM Mutations +permalink: '/guide/external-dom-mutations' +description: 'How to integrate Preact with jQuery and other JavaScript snippets which mutate the DOM directly' +--- + +# External DOM Mutations + +Sometimes there is a need to work with third-party libraries that expect to be able to freely mutate the DOM, persist state within it, or that have no component boundaries at all. There are many great UI toolkits or re-usable elements that operate this way. + +In Preact (and similarly in React), working with these types of libraries requires that you tell the Virtual DOM rendering/diffing algorithm that it shouldn't try to _undo_ any external DOM mutations performed within a given Component (or the DOM element it represents). + +--- + +
    + +--- + +## Technique + +This can be as simple as defining a `shouldComponentUpdate()` method on your component, and having it return `false`: + +```jsx +class Block extends Component { + shouldComponentUpdate() { + return false; + } +} +``` + +... or for shorthand: + +```jsx +class Block extends Component { + shouldComponentUpdate = () => false; +} +``` + +With this lifecycle hook in place and telling Preact not to re-render the Component when changes occur up the VDOM tree, your Component now has a reference to its root DOM element that can be treated as static until the Component is unmounted. As with any component that reference is simply called `this.base`, and corresponds to the root JSX Element that was returned from `render()`. + +--- + +## Example Walk-Through + +Here is an example of "turning off" re-rendering for a Component. Note that `render()` is still invoked as part of creating and mounting the Component, in order to generate its initial DOM structure. + +```jsx +class Example extends Component { + shouldComponentUpdate() { + // do not re-render via diff: + return false; + } + + componentDidMount() { + // now mounted, can freely modify the DOM: + let thing = document.createElement('maybe-a-custom-element'); + this.base.appendChild(thing); + } + + componentWillUnmount() { + // component is about to be removed from the DOM, perform any cleanup. + } + + render() { + return
    ; + } +} +``` + + +## Demonstration + +[![demo](https://i.gyazo.com/a63622edbeefb2e86d6c0d9c8d66e582.gif)](http://www.webpackbin.com/V1hyNQbpe) + +[**View this demo on Webpackbin**](https://www.webpackbin.com/bins/-KflCmJ5bvKsRF8WDkzb) + + +## Real-World Examples + +Alternatively, see this technique in action in [preact-token-input](https://github.com/developit/preact-token-input/blob/master/src/index.js) - it uses a component as a foothold in the DOM, but then disables updates and lets [tags-input](https://github.com/developit/tags-input) take over from there. A more complex example would be [preact-richtextarea](https://github.com/developit/preact-richtextarea), which uses this technique to avoid re-rendering an editable `