Jak poprawnie uaktualnić state w ponownym renderze?

0

Hej, tworze edytor RichText i mam nastepujący problem. Ten komponent re-renderuje sie przy kazdym kliknieciu i wyglada nastepuajco:


export function Editor() {
  const editor = useEditor({
    extensions: [StarterKit, Link.configure({ openOnClick: false })],
    editorProps: {
      attributes: {
        class: "Editor",
      },
    },
    content: `
      <h1>Welcome to your fresh Tiptap Code Sandbox</h1>
      <p>See us <a href="https://www.google.com/">here<a/></p>
    `,
  });

  const [open, setOpen] = useState(false);

  useEffect(() => {
    if (!editor) return;
    setOpen(!editor.state.selection.empty);
  }, [editor?.state]);

  console.log(open);
  return (
    <div>
      {editor && (
        <ControlledBubbleMenu
          editor={editor}
          // open={!editor.state.selection.empty}
          open={open}
          // open={true}
        >
          <span style={{ backgroundColor: "red" }}>Bubble Menu Content</span>
        </ControlledBubbleMenu>
      )}
      <EditorContent editor={editor} />
      {/* {editor && <LinkBubbleMenu editor={editor} />} */}
    </div>
  );
}

Wprowadzilem tutaj state open, ktorego poczatkowa wartosc wynosi !editor.state.selection.empty. Chcialbym zeby ten state byl updateowany przy kazdym rerenderingu. Jesli zrobie tak:

  const [open, setOpen] = useState(() => !editor.state.selection.empty);

to niestety open nie bedzie sie updatowalo. Wprowadzilem wiec useEffect

  useEffect(() => {
    if (!editor) return;
    setOpen(!editor.state.selection.empty);
  }, [editor?.state]);

I to dziala poprawnie. Pytanie tylko czy dokladnie tak powinno sie to rozwiazac czy jest poprawniejszy sposob? Chcialbym dodatkowo wprowadzic analogiczne rozwiazanie dla stateow ktorych poczatkowa wartosc wyznaczana jest przez prop pdoawany do komponentu Editor. Czyli jesli np. Editor przyjmuje defaultContent jako prop to czy poprawnym rozwiazaniem jest
ponizszy kod

  const [content, setContent] = useState(() => defaultContent);

czy analogicznie musze zrobic tak:

  useEffect(() => {
    setContent(defaultContent);
  }, [defaultContent]);
0

Daj jakiś link do sandboxa, co to za richtext editor

Żeby zaktualizować state "przy każdym renderingu" to w ogóle nie potrzebujesz state, wystarczy

const open = !editor.state.selection.empty;

Tylko czy wtedy będzie coś co będzie wywoływało rerendering

Kokos12345 napisał(a):
  const [content, setContent] = useState(() => defaultContent);

czy analogicznie musze zrobic tak:

  useEffect(() => {
    setContent(defaultContent);
  }, [defaultContent]);

to zależy jaki efekt chcesz uzyskać. Jak tak zrobisz to zmiana defaultContent nadpisze content nawet jeśli został zmieniony w innym miejscu. Czy tak powinno być to wiesz tylko ty

1

Skąd bierzesz useEditor? Z jakiejś konkretnej biblioteki, czy to twój hook?

Bo zależy, jak to jest zaimplementowane. Czy sam useEditor powoduje już rerender komponentu?

Jeśli tak, to pewnie faktycznie nie potrzebowałbyś wcale dodatkowego state.

0
LukeJL napisał(a):

Skąd bierzesz useEditor? Z jakiejś konkretnej biblioteki, czy to twój hook?

Bo zależy, jak to jest zaimplementowane. Czy sam useEditor powoduje już rerender komponentu?

Jeśli tak, to pewnie faktycznie nie potrzebowałbyś wcale dodatkowego state.

https://github.com/ueberdosis/tiptap/blob/develop/packages/react/src/useEditor.ts

Tak powoduje rerender. Kwestia jest tego typu ze open ma defaultowo zalezec od !editor.state.selection.empty (czyli jesli ktos zaznaczy tekst to ma sie pojawic popover z menu) ale chce moc go rownez nadpisac przez inne komponenty, jak np. ustawic na true, czyli nie zamykac popovera nawet jesli ktos kliknie gdzies indziej i juz tekst nie bedzie wybrany.

0
Kokos12345 napisał(a):

https://github.com/ueberdosis/tiptap/blob/develop/packages/react/src/useEditor.ts

Tak powoduje rerender. Kwestia jest tego typu ze open ma defaultowo zalezec od !editor.state.selection.empty (czyli jesli ktos zaznaczy tekst to ma sie pojawic popover z menu) ale chce moc go rownez nadpisac przez inne komponenty, jak np. ustawic na true, czyli nie zamykac popovera nawet jesli ktos kliknie gdzies indziej i juz tekst nie bedzie wybrany.

No to można zrobić tak jak ty zrobiłeś, ale z tego co widzę ten edytor wystawia event onSelectionUpdate i skorzystałbym z niego zamiast z useEffect, bo teraz niepotrzebnie renderujesz komponent po dwa razy (po zmianie zaznaczenia komponent się rerenderuje po to żeby wywołać useEffect gdzie znowu coś zmieniasz i doprowadzasz do kolejnego rendera)

0
obscurity napisał(a):
Kokos12345 napisał(a):

https://github.com/ueberdosis/tiptap/blob/develop/packages/react/src/useEditor.ts

Tak powoduje rerender. Kwestia jest tego typu ze open ma defaultowo zalezec od !editor.state.selection.empty (czyli jesli ktos zaznaczy tekst to ma sie pojawic popover z menu) ale chce moc go rownez nadpisac przez inne komponenty, jak np. ustawic na true, czyli nie zamykac popovera nawet jesli ktos kliknie gdzies indziej i juz tekst nie bedzie wybrany.

No to można zrobić tak jak ty zrobiłeś, ale z tego co widzę ten edytor wystawia event onSelectionUpdate i skorzystałbym z niego zamiast z useEffect, bo teraz niepotrzebnie renderujesz komponent po dwa razy (po zmianie zaznaczenia komponent się rerenderuje po to żeby wywołać useEffect gdzie znowu coś zmieniasz i doprowadzasz do kolejnego rendera)

Dzieki! Nie mam jeszcze doswiadczenia w efektywnym pisaniu kodu i pojawil sie inny problem. Tym razem dla ulawienia zalaczam CodeSandBox (https://codesandbox.io/p/sandbox/controlled-bubble-menu-framer-4programmers-9z7t4s?file=%2Fsrc%2Fbubble-menu.tsx%3A20%2C58). Mianowicie, chce stworzyc BubbleMenu ktory ma miec nastepujace wlasnosci:

  • pojawia sie tylko jesli jakis tekst jest zaznaczony
  • ladnie sie animuje przy pojawianiu, znikaniu i rozszerzaniu wybranego tekstu
  • znika jesli edytor traci focus ale nie znika jesli jednoczesnie to menu ma ten focus

Do stworzenia tego komponentu uzywam floating-ui i framer-motion do animacji. W chwili obecnej widze nastepujace bledy i bede bardzo wdzieczny za pomoc:

  • jesli zaznacze "Welcome" i klikne w menu, to menu sie rusza i blyskawicznie wraca na swoje miejsce, prawdopodobnie przez to ze najpierw open jest ustawiane na false zeby zaraz pozniej byc ustawione na true. To czy menu jest otwarte kontrolowane jest zarowno w komponencie BubbleMenu jak i samym edytorze poprzez onBlur() i onSelectionUpdate i zakladam ze to stad wynikaja problemy. Jak to elegancko poprawic? Generalnie komponent ma byc resusable czyli takich menu chce miec np. 10 roznych (dla linkow, obrazkow itp) wiec tak naprawde jedyne czymsie maja roznic to kontentem i kiedy maja sie pojawiac (przy czym wszystkie maja znikac na blur i nie znikac kiedy sie na nie kliknie)
  • Jesli zaznacze "Welcome" i klkne gdziekolwiek indziej to menu ladnie znika. Jesli natomiast zaznacze "Welcome" a potem zaznacze "Sandbox" a potem klikne gdziekolwiek indziej to menu zamiast zniknac tak jak zawsze z translateY(5px) to wzcesniej zmienia swoja pozycje tam gdzie kliknalem. Jak to poprawic?

1 użytkowników online, w tym zalogowanych: 0, gości: 1