When calling client.enter()
, you stumble upon the following error:
The @liveblocks/client
package expects to be consumed by a JavaScript bundler,
like Webpack, Babel, ESbuild, Rollup, etc. If you see this error, you have most
likely directly loaded the @liveblocks/client
source code through a <script>
tag.
If using a bundler isn’t possible for you, there are two available solutions.
One solution is to load the source code from the
Skypack CDN, which will
expose optimized production builds which have the process
variable removed.
Another solution is to define the necessary process.env.NODE_ENV
variable
manually, before loading the script, for example:
If you’re on React 17 or lower, there is a well-known problem that all state management libraries need to deal with at some point, known as the "Stale-props / zombie-child" problem—Liveblocks is no exception.
This issue can cause catastrophic bugs, inconsistent renders, or—in the best case—performance issues. Sooner or later, as your app grows in complexity, you will run into some manifestation of this bug, and we want to be ahead of that.
Just do either of the following to avoid it!
unstable_batchedUpdates
function to the RoomProvider in the mean time, so
Liveblocks can circumvent the issue on your behalf!Starting with 0.18.3, this will be enforced in React 17 and lower, potentially saving hours of debugging.
If you’re on React 17 or lower, state updates from “external” (non-DOM) events will not get batched together automatically. If two or more of your components subscribe to the same piece of state, and this state gets updated by another user in the room, then both components will individually rerender, separately.
In most cases this is just inefficient but not catastrophic. However, if you happen to have a parent and child component that both rely on the same state, this can lead to hard-to-debug bugs.
For example:
Both of these components need to get rerendered if, for example, some shape gets deleted by another user in the room.
In React 17 (or lower), those rerenders will not get batched together and as such they will not rerender together. Instead, they rerender individually, separately. Which component rerenders first is undefined and often unpredictable in larger apps. If you’re unlucky, this can lead to the Child component to get rerendered before its Parent has had the opportunity to unmount it, which should of course never happen.
By providing unstable_batchedUpdates
to the RoomProvider, Liveblocks will wrap
all state updates in this helper, which will make sure that both Parent and
Child get rerendered as part of the same render cycle. This way, React will
ensure that the Parent component will always get rerendered before the Child.
If you found this page, chances are you stumbled upon this TypeScript error:
Liveblocks data structures (like LiveObject
, LiveMap
, and LiveList
)
require that their payloads are always JSON-serializable to be able to send them
over WebSocket connections reliably and without surprises. Starting with 0.16,
we’re enforcing this with Lson
type constraint. (LSON is a Liveblocks-specific
extension of JSON that also allows nesting more Live data structures.)
If you encounter this error above, TypeScript is trying to tell you that the data type you are using in one of your Live structures is not (guaranteed to be) a legal LSON (or JSON) value. But why?
Although this Person
type seems perfectly JSON-serializable with only those
string
and number
fields, TypeScript still considers this a problem because
it cannot guarantee that all of its subtypes will also be that. Interface
types are "open" and extensible by design. This means it’s possible to define a
subtype that would still not be JSON-serializable.
Example.
To fix this issue, there are roughly three available solutions.
The simplest solution is to convert your interface
to a type
.
Check this solution out in the TypeScript playground.
You can also explicitly pledge that your interface will be JSON serializable by
having it extend from JsonObject
.
Check this solution out in the TypeScript playground.
This is the least preferred solution, but may be necessary if you don’t own the interface definition and it’s coming from an external package.
Check this solution out in the TypeScript playground.