measuredco / puck

The visual editor for React
https://puckeditor.com
MIT License
5.11k stars 289 forks source link

Loosing focus and unexpected inputs when using text fields inside array fields #622

Open FedericoBonel opened 1 week ago

FedericoBonel commented 1 week ago

Background

puck version: 0.16.0

Description

Currently, there's a problem with text fields nested in array fields where, whenever the user types on them, it sometimes re-renders and loads, loosing focus and making the cursor go all the way to the end of the current input and even delete parts of what I just typed.

Current behavior

Whenever you use an array field with some type of text input, there seems to be a full re-render of all the array items in that field on every change event, making the field lose focus and regain it, resetting the cursor to the end of the field and sometimes loosing parts of the input. This specially happens when you start having a lot of items in the array field or nested array fields one inside another.

Expected behavior

The text field should not lose focus and or parts of the input because of re-renders.

Steps to reproduce

  1. Create a config file with an array field and nested text fields. For example (Using the next.js recipe):
import type { Config, WithId, WithPuckProps } from "@measured/puck";

type Props = {
    HeadingBlock: { title: string };
    // The array field with items
    ArrayTextField: { items: { content: string }[] };
};

export const config: Config<Props> = {
    components: {
        HeadingBlock: {
            fields: {
                title: { type: "text" },
            },
            defaultProps: {
                title: "Heading",
            },
            render: ({ title }) => (
                <div style={{ padding: 64 }}>
                    <h1>{title}</h1>
                </div>
            ),
        },
        // The array field here
        ArrayTextField: {
            fields: {
                items: {
                    getItemSummary: (item) => item.content,
                    type: "array",
                    arrayFields: {
                        // The text field here
                        content: { type: "text" },
                    },
                },
            },
            defaultProps: { items: [] },
            render: ({ items: array }) => (
                <div>
                    {array.map((item) => (
                        <p>{item.content}</p>
                    ))}
                </div>
            ),
        },
    },
};

export default config;
  1. Type something quickly in the text field or input something and try to place the text cursor at the beginning of the input and type:

Puck .webm