docs: updates building your own live preview hook (#3604)

This commit is contained in:
Jacob Fletcher
2023-10-12 14:20:02 -04:00
committed by GitHub
parent 05eba56d7d
commit 15c7f0dbf3
2 changed files with 31 additions and 9 deletions

View File

@@ -88,13 +88,14 @@ This package provides the following functions:
| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`subscribe`** | Subscribes to the Admin panel's `window.postMessage` events and calls the provided callback function. | | **`subscribe`** | Subscribes to the Admin panel's `window.postMessage` events and calls the provided callback function. |
| **`unsubscribe`** | Unsubscribes from the Admin panel's `window.postMessage` events. | | **`unsubscribe`** | Unsubscribes from the Admin panel's `window.postMessage` events. |
| **`ready`** | Sends a `window.postMessage` event to the Admin panel to indicate that the front-end is ready to receive messages. |
The `subscribe` function takes the following args: The `subscribe` function takes the following args:
| Path | Description | | Path | Description |
| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`callback`** \* | A callback function that is called with `data` every time a change is made to the document. | | **`callback`** \* | A callback function that is called with `data` every time a change is made to the document. |
| **`serverURL`** \* | The URL of your Payload server. git s | | **`serverURL`** \* | The URL of your Payload server. |
| **`initialData`** | The initial data of the document. The live data will be merged in as changes are made. | | **`initialData`** | The initial data of the document. The live data will be merged in as changes are made. |
| **`depth`** | The depth of the relationships to fetch. Defaults to `0`. | | **`depth`** | The depth of the relationships to fetch. Defaults to `0`. |
@@ -103,18 +104,23 @@ With these functions, you can build your own hook using your front-end framework
```tsx ```tsx
import { subscribe, unsubscribe } from '@payloadcms/live-preview'; import { subscribe, unsubscribe } from '@payloadcms/live-preview';
// Build your own hook to subscribe to the live preview events // To build your own hook, subscribe to Live Preview events using the`subscribe` function
// This function will handle everything for you like // It handles everything from:
// 1. subscribing to `window.postMessage` events // 1. Listening to `window.postMessage` events
// 2. merging initial page data with incoming form state // 2. Merging initial data with active form state
// 3. populating relationships and uploads // 3. Populating relationships and uploads
// 4. Calling the `onChange` callback with the result
// Your hook should also:
// 1. Tell the Admin panel when it is ready to receive messages
// 2. Handle the results of the `onChange` callback to update the UI
// 3. Unsubscribe from the `window.postMessage` events when it unmounts
``` ```
Here is an example of what the same `useLivePreview` React hook from above looks like under the hood: Here is an example of what the same `useLivePreview` React hook from above looks like under the hood:
```tsx ```tsx
import { subscribe, unsubscribe } from '@payloadcms/live-preview' import { subscribe, unsubscribe, ready } from '@payloadcms/live-preview'
import { useCallback, useEffect, useState } from 'react' import { useCallback, useEffect, useState, useRef } from 'react'
export const useLivePreview = <T extends any>(props: { export const useLivePreview = <T extends any>(props: {
depth?: number depth?: number
@@ -127,13 +133,18 @@ export const useLivePreview = <T extends any>(props: {
const { depth = 0, initialData, serverURL } = props const { depth = 0, initialData, serverURL } = props
const [data, setData] = useState<T>(initialData) const [data, setData] = useState<T>(initialData)
const [isLoading, setIsLoading] = useState<boolean>(true) const [isLoading, setIsLoading] = useState<boolean>(true)
const hasSentReadyMessage = useRef<boolean>(false)
const onChange = useCallback((mergedData) => { const onChange = useCallback((mergedData) => {
// When a change is made, the `onChange` callback will be called with the merged data
// Set this merged data into state so that React will re-render the UI
setData(mergedData) setData(mergedData)
setIsLoading(false) setIsLoading(false)
}, []) }, [])
useEffect(() => { useEffect(() => {
// Listen for `window.postMessage` events from the Admin panel
// When a change is made, the `onChange` callback will be called with the merged data
const subscription = subscribe({ const subscription = subscribe({
callback: onChange, callback: onChange,
depth, depth,
@@ -141,6 +152,17 @@ export const useLivePreview = <T extends any>(props: {
serverURL, serverURL,
}) })
// Once subscribed, send a `ready` message back up to the Admin panel
// This will indicate that the front-end is ready to receive messages
if (!hasSentReadyMessage.current) {
hasSentReadyMessage.current = true
ready({
serverURL
})
}
// When the component unmounts, unsubscribe from the `window.postMessage` events
return () => { return () => {
unsubscribe(subscription) unsubscribe(subscription)
} }

View File

@@ -85,7 +85,7 @@ Here is an example of using a function that returns a dynamic URL:
locale locale
}) => `${data.tenant.url}${ // Multi-tenant top-level domain }) => `${data.tenant.url}${ // Multi-tenant top-level domain
documentInfo.slug === 'posts' ? `/posts/${data.slug}` : `${data.slug !== 'home' : `/${data.slug}` : ''}` documentInfo.slug === 'posts' ? `/posts/${data.slug}` : `${data.slug !== 'home' : `/${data.slug}` : ''}`
`}?locale=${locale}`, // Localization query param }${locale ? `?locale=${locale?.code}` : ''}`, // Localization query param
collections: ['pages'], collections: ['pages'],
}, },
} }