Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

@contentful/field-editor-rich-text re-rendering problem #1173

Open
giovanni-caiazzo opened this issue Jul 4, 2022 · 6 comments
Open

@contentful/field-editor-rich-text re-rendering problem #1173

giovanni-caiazzo opened this issue Jul 4, 2022 · 6 comments
Labels
stale Used to mark when there was no activity for a set period of time

Comments

@giovanni-caiazzo
Copy link

Hi all,
I am using @contentful/field-editor-rich-text in a contentful app, version 2.1.0 and I have some problems with rerendering.
Basically I am rendering a set of tabs and each tab has an instance of @contentful/field-editor-rich-text:

<ConnectedRichTextEditor
sdk={sdk}
value={accordion.content}
onChange={(e: any) =>
setGlobalData((prevState) => {
    const newState = [...prevState];
    newState[idx].content = e;
    return newState;
})}
minHeight={"300px"}
/>

When the component first renders it works great, but as soon as I change tab, the editor disappears and never reappears even when I return to the original tab. When the page is reloaded the editor reappears, only to disappear again as soon as a new tab is selected.
I also tried to render all the editors together without tabs: what happens is that all the editors have the content of the last rendered editor, which is not ideal.

Here is the full Field component

const Field = () => {
  const sdk = useSDK<FieldExtensionSDK>();
  const [globalData, setGlobalData] = useState<AccordionData[]>(
    sdk.field.getValue() || [{ title: "", content: emptyRTF }]
  );
  const [currentTab, setCurrentTab] = useState("accordion-0");

  useEffect(() => {
    sdk.window.startAutoResizer();
  });

  useEffect(() => {
    if (globalData && sdk.field.getValue() !== globalData) {
      sdk.field.setValue(globalData).then();
    }
  }, [globalData, sdk.field]);

  return (
    <>
      <Note style={{ marginTop: "10px", marginBottom: "20px" }}>
        You can create more than one accordion in this component. Use "Add
        Accordion" to create one. If you want to delete a particular accordion
        you can use the "Delete Accordion" button of the relative Accordion.
      </Note>
      <div
        style={{
          display: "flex",
          justifyContent: "center",
          alignItems: "center",
        }}
      >
        <Button
          style={{ marginBottom: "20px" }}
          variant="primary"
          onClick={() => {
            setGlobalData((prevState) => [
              ...prevState,
              { title: "", content: emptyRTF },
            ]);
            const currentIdx = parseInt(currentTab.split("-")[1]);
            setCurrentTab(`accordion-${currentIdx + 1}`);
          }}
        >
          Add Accordion
        </Button>
      </div>
      <Tabs currentTab={currentTab} onTabChange={setCurrentTab}>
        <Tabs.List>
          {globalData.map((accordion, idx) => {
            return (
              <Tabs.Tab panelId={`accordion-${idx}`} key={`accordion-${idx}`}>
                A. # {idx + 1}
              </Tabs.Tab>
            );
          })}
        </Tabs.List>

        {globalData.map((accordion, idx) => {
          return (
            <Tabs.Panel id={`accordion-${idx}`} key={`accordion-${idx}`}>
              <h2 style={{ marginTop: "20px", marginBottom: "10px" }}>Title</h2>
              <TextInput
                value={accordion.title}
                onChange={(e: any) =>
                  setGlobalData((prevState) => {
                    const newState = [...prevState];
                    newState[idx].title = e.target.value;
                    return newState;
                  })
                }
                title={"Header"}
                placeholder={"Header"}
              />
              <h2 style={{ marginTop: "30px", marginBottom: "10px" }}>
                Content
              </h2>
              <ConnectedRichTextEditor
                // @ts-ignore
                sdk={sdk}
                value={accordion.content}
                onChange={(e: any) =>
                  setGlobalData((prevState) => {
                    const newState = [...prevState];
                    newState[idx].content = e;
                    return newState;
                  })
                }
                minHeight={"300px"}
              />
              <div
                style={{
                  display: "flex",
                  justifyContent: "end",
                  alignItems: "center",
                }}
              >
                <Button
                  style={{ marginTop: "20px" }}
                  variant="negative"
                  onClick={() => {
                    setGlobalData((prevState) => {
                      const newData = [...prevState];
                      newData.splice(idx, 1);
                      return newData;
                    });
                    const currentIdx = parseInt(currentTab.split("-")[1]);
                    setCurrentTab(
                      `accordion-${currentIdx >= 1 ? currentIdx - 1 : 0}`
                    );
                  }}
                >
                  Delete Accordion
                </Button>
              </div>
            </Tabs.Panel>
          );
        })}
      </Tabs>
    </>
  );
};

Is there something I'm doing wrong when rendering the editors? Or is this a bug of the editor? Version 1 did not have this problem using the same code structure, but it did have problems selecting the text and changing it.

@niclaszllaudi
Copy link

Hi, I encountered the same issue with basically the same use case. After some debugging I tracked down the issue to the ids of the respective ConnectedRichTextEditors. Internally they are passed to the Plate component, which requires the ids to be unique. I was able to fix the issue by passing a unique id to our custom ConnectedRichTextEditor implementation which replaces the internal id calculation.

Unfortunately there doesn't seem to be a fix available without rewriting the internal rich text editor logic.

@giovanni-caiazzo
Copy link
Author

Thank you so much @niclaszllaudi for pinpointing the issue in the codebase. I have used patch-package to make it work. This however is NOT a solution to the issue and if the devs want us to make a PR I would happily do it. For now my project is not blocked though, which is great.

@z0al
Copy link
Member

z0al commented Jul 12, 2022

I was able to fix the issue by passing a unique id to our custom ConnectedRichTextEditor implementation which replaces the internal id calculation.

Can you share more about your use case @niclaszllaudi & @giovanni-caiazzo ?

For the internal ids calculation we use the entry key + field id + the locale code. So, in theory, it should only clash if you are rendering the same entry field for the same locale twice. Is that the case for you?

@giovanni-caiazzo
Copy link
Author

Hi @z0al. In my use case I have a contentful app that is rendering a json field as a collection of rich text fields, each accessible with its own tab. I used patch-package to add a new prop to ConnectedRichEditor in order to add a custom ID appended to the ID you are creating like you described.

@niclaszllaudi
Copy link

Hi @z0al, we want to render two ConnectedRichTextEditor instances within the same field. This results in both being assigned the same id, causing the issue.

@github-actions
Copy link

Marking issue as stale since there was no acitivty for 30 days

@github-actions github-actions bot added the stale Used to mark when there was no activity for a set period of time label Aug 13, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
stale Used to mark when there was no activity for a set period of time
Projects
None yet
Development

No branches or pull requests

3 participants