puck icon indicating copy to clipboard operation
puck copied to clipboard

Dispatch does not update array fields correctly

Open benlife5 opened this issue 1 month ago • 1 comments

Description

When using the replace action of dispatch, the array fields in the sidebar do not update for the selected environment

We have custom copy/paste functionality that saves a component's props to the clipboard on copy and dispatches a replace action on paste. I'm running into an issue when pasting array fields -- the component's actual props update correctly but the fields in the sidebar do not.

Environment

  • Puck version: 0.0.20

Steps to reproduce

  1. Add this component
Test: {
    label: "Test",
    fields: {
      example: {
        type: "custom",
        render: () => {
          const getPuck = useGetPuck();
          const copyToClipboard = () => {
            const { appState, getItemBySelector } = getPuck();

            if (!appState.ui.itemSelector) {
              return;
            }

            const selectedComponent = getItemBySelector(
              appState.ui.itemSelector
            );
            navigator.clipboard.writeText(
              JSON.stringify(selectedComponent, null, 2)
            );
          };

          const pasteFromClipboard = async () => {
            const { appState, dispatch, getItemBySelector } = getPuck();

            const rawClipboardText = await navigator.clipboard.readText();
            const pastedData = JSON.parse(rawClipboardText);
 
            if (!appState.ui.itemSelector || !appState.ui.itemSelector.zone) {
              return;
            }

            const selectedComponent = getItemBySelector({
              index: appState.ui.itemSelector?.index,
              zone: appState.ui.itemSelector?.zone,
            });

            dispatch({
              type: "replace",
              destinationZone: appState.ui.itemSelector.zone,
              destinationIndex: appState.ui.itemSelector.index,
              data: {
                type: pastedData.type,
                props: {
                  ...pastedData.props,
                  id: selectedComponent?.props.id,
                },
              },
            });
          };

          return (
            <div>
              <button
                onClick={copyToClipboard}
                style={{ margin: 8, padding: 8, border: "2px solid black" }}
              >
                Copy
              </button>
              <button
                onClick={pasteFromClipboard}
                style={{ margin: 8, padding: 8, border: "2px solid black" }}
              >
                Paste
              </button>
            </div>
          );
        },
      },
      list: {
        type: "array",
        arrayFields: {
          text: {
            type: "text",
          },
        },
        getItemSummary: (item: any, index?: number) => {
          return item.text || `Item ${index}`;
        },
      },
    },
    render: (props) => {
      return (
        <div style={{ margin: 16 }}>
          {props?.list?.length ? (
            props.list.map((l: any) => l.text).join(",")
          ) : (
            <div style={{ height: 32 }} />
          )}
        </div>
      );
    },
    defaultProps: {
      list: [],
    },
  },
  1. Drag two instances of the component onto the page
  2. Add n pieces of data to the first component and n-1 pieces of data to the second component
  3. Copy the second component and paste into the first component

What happens

The actual props/rendering for the component update correctly but the fields in the sidebar do not.

What I expect to happen

The data in the sidebar and the data being rendered should match.

Additional Media

https://jam.dev/c/0ebba475-ed44-4e8f-9327-afbbf57eb576

benlife5 avatar Nov 13 '25 14:11 benlife5

Hey @benlife5! Thanks for opening this issue and reporting the bug.

I can confirm that I can reproduce this with Puck 0.20.2.

Interestingly this doesn't happen if the paste target is empty.

This PR might resolve the issue once it's finished, mentioning it here as a reminder to test this again once it's more mature: #1213

FedericoBonel avatar Nov 18 '25 05:11 FedericoBonel