• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

[React] Custom hook bez przeładowania rodzica

+1 głos
765 wizyt
pytanie zadane 31 stycznia 2022 w JavaScript przez Oskar Szkurłat Bywalec (2,780 p.)

Cześć,

Chciałem sobie uwspółcześnić kod, bo miałem wrapper komponentu (taki ala HOC), który zwracał callback z obiektem z referencjami do zmieniania stanów wewnątrz tego wrappera. I to działało, dokładnie tak jak chciałem, czyli miałem np. buttonComponent.setText('nowy tekst'). Po wywołaniu metody przychodzącej z callbacka, odświeżany był z nowym napisem tylko przycisk, a rodzic trzymający go już nie.

Postanowiłem zamienić wrapper w customowego hooka. Jednak nie przewidziałem, że stany z customowego hooka są trzymane w rodzicu? Tak przynajmniej najwyraźniej sądzę, bo przy przerenderowaniu teraz przycisku, przerenderowywany jest również rodzic :(.

const buttonToChangePattern = useAntPattern(buttonToChangeOptions.current)

Czy ktoś ma pomysł, jak mógłbym to zabezpieczyć, żeby przeładowywał się tylko np. przycisk, a div trzymający różne przyciski nie? Bo nie chciałbym robić specjalnie teraz otoczki w postaci kolejnego komponentu, bo wtedy ten hook mija mi się z celem.

Czy da się sprawić, żeby customowy hook działał, jak komponent, tzn. miał swoje oddzielne pamięci i przy zmianie zmieniał się tylko on? Bo wydawało mi się, że tak powinno to działać w Reacie. Na różnych filmikach widziałem, że można bez problemu z HOC przejść na hook.

Link do kodu na Git Hubie:
https://github.com/Amaranthusss/ant-design-patterns

Przykładowo w komponencie ButtonExample.ts można postawić breakpoint na return i wtedy widać, że przy przerenderowaniu przycisku (który jest zwracany z hooka), przeładowuje się cały ButtonExample.ts

A może jeszcze w czymś innym robię błąd i stąd te przeładowanie rodzica?

Z góry dzięki.

1 odpowiedź

+3 głosów
odpowiedź 1 lutego 2022 przez rafal.budzis Szeryf (86,520 p.)
wybrane 2 lutego 2022 przez Oskar Szkurłat
 
Najlepsza
Musisz sobie zadać pytanie co robił HOC. A HOC tworzy często nowy komponent i przekazuje zmodyfikowane propsy. Więc bez tworzenia nowego komponentu się nie obędzie również w przypadku hooka.

Komponent w którym jest wywoływołanie hooka zawsze się ponownie wyrenderuje gdy zmieni się w jego state.

Jak chcesz coś jeszcze dopytać to śmiało. Nie podaje przykładu ponieważ powiem szczerze że nie chce mi się szukać tego kodu w tym repo. Jak coś wrzuć konkretny kod z 2 plików rodzic > dziecko to może będę w stanie podpowiedzieć coś więcej ;)

Ogólnie w React im więcej małych komponentów tym lepiej ;)
komentarz 1 lutego 2022 przez Oskar Szkurłat Bywalec (2,780 p.)
edycja 1 lutego 2022 przez Oskar Szkurłat

Oki, po doczytaniu w dokumentacji Reacta i z tego co tutaj napisałeś, faktycznie wychodzi na to, że customowe hooki, mimo że są odrębną funkcją, w tym przypadku zwracającą komponent, stany przechowują w rodzicu. Stąd te przerenderowania. Bo customowy hook to dalej hook, czyli tylko funkcja bez ciała.

Mój hook z założenia miał zwracać komponent (jako parametr element) i obiekt z referencjami do metod zmieniającymi stany w tym komponencie i tylko w nim, nazwany controller.

Podlinkowuję przykład, gdzie widać, w jaki sposób ja korzystałem, tzn. tu jest rodzic ButtonExample, który ma w sobie dwa wywołania mojego customowego hooka useAntPattern. Czyli zamysł był taki, że tworzę dwa komponenty wewnątrz tego rodzica i steruję z poziomu rodzica dziećmi i ich przerenderowaniami w środku. Przycisk buttonToChangeIconPattern po wciśnięciu zmienia ikonkę w buttonToChangePattern. I są to te dzieci-komponenty.

Więc teraz rozumiem, że bez budowania zwykłego komponentu, jako wrapper/pattern/hoc (w sumie o to może chcę dopytać, jakie nazewnictwo jest poprawne dla reacta najbardziej dla definicji komponentu uniwersalnego, a może używa się ich wszystkich i coś innego oznaczają? W stylu klasy z OOP, gdzie konfigurujesz wartości stałe (tu przez props) i komponent potem żyje swoim życiem, zarządza swoimi pamięciami i można go wywoływać w wielu miejscach i konfigurować do potrzeb wywołania. Przykładem może być tabelka z przyciskami w toolbaru, gdzie na stronie A jest przycisków np. 4, a na stronie B przycisków 7, w zależności od potrzeb. A mimo to unika się powielania kodu i całej jego logiki przez użycie takiego komponentu.

A może masz pomysł na jeszcze inne, eleganckie rozwiązanie przerobienia hooka, tak aby zwracał komponenty z odrębnymi obszarami swoich pamięci? Bo staram się nauczyć dobrych nawyków Reactowych, bo póki co programuję na zasadzie "ja bym zrobił to tak". :D

Super odpowiedź, dziękuję :)

1
komentarz 2 lutego 2022 przez rafal.budzis Szeryf (86,520 p.)

O kurczę racja twój hook w tej chwili jest nieco bez sensu bo bardziej przypomina komponent niż hooka. I wydaje mi się że było by łatwiej gdybyś go przerobił po prostu na komponent :) 

W react nie musisz samemu sterować renderem dzieci. Zostaw to reactowi! React za każdym razem renderuje dzieci gdy przekazujesz nowe propsy ;) 

Hooki są po to aby w komponencie było mniej logiki :) Tutaj mały przykłąd jak można to wykorzystać ;) 

 

import { ChangeEvent, useCallback, useRef, useState } from 'react'
import { Tooltip } from 'antd'
import _ from 'lodash'

import {
  IPatternOptions,
  IValue,
} from './interface';

// hook
const usePreparedOnChangeCallback = (props: IPatternOptions<any>) => {
    const [value, setValue] = useState<IValue>()

    const onChange = useCallback(
      (event: ChangeEvent<any>): void => {
        setValue(event.target.value)
        if (_.isFunction(props.default?.onChange)) {
          props.default?.onChange(event)
        }
      },
      [props.default]
    )

    return {onChange, value}
}

// component
const AntPatternComponent = (props: IPatternOptions<any>) => {
    const [options, setOptions] = useState<any>(props.default ?? {})
    const [tooltipOptions, setTooltipOptions] = useState<any>(
        props.tooltipOptions ?? ({} as any)
    )
    const {onChange, value} = usePreparedOnChangeCallback(props);

    return  <Tooltip {...tooltipOptions}>
        <props.element {...options} onChange={onChange} value={value}>
          {options?.children ?? undefined}
        </props.element>
    </Tooltip>

}

export default AntPatternComponent

Zwróć uwagę że dzięki użyciu hooka w komponencie nie jest dostępna funkcja setValue więc masz lepszą separacje/hermetyzacje ;) 

komentarz 18 lutego 2022 przez Oskar Szkurłat Bywalec (2,780 p.)

W sumie teraz mi nasunęło się pytanie - w jaki sposób Ty proponujesz układać hooki w projekcie w takim przypadku? Bo za pomocą customowego hooka zapewniłem hermetyzację, ale on dotyczy tylko tego komponentu.

Na ogół widziałem, że customowe hooki trzyma się w globalnym folderze hooks. Ale gdy tam trzymać będę hook dotyczący tego komponentu, to czy to nie jest zbyt duży rozrzut po projekcie? Lepiej trzymać customowe hooki w folderze komponentu, którego dotyczą? A globalne / uniwersalne hooki w tym folderze hooks?

Jeżeli tak, to czy dobrym będzie według Ciebie standardem stworzenie jednego pliku eksportującego wszystkie custom hooki dla tego komponentu? np. AntPattern.hooks.ts, a w nim export const useOnInputChanged = (...) => {...} ?

 

1
komentarz 18 lutego 2022 przez rafal.budzis Szeryf (86,520 p.)

Odnośnie układania plików obowiązuje jedna zasada. Zmieniał ułożenie plików aż nie poczujesz że jest dobrze ;) Mi się bardzo podoba koncepcja trzymania globalnych hooków w hooks a customowe odnoszące się do jednego komponentu w folderze tego komponentu. Sam tak robię i polecam ;) 

Co do plików ja wole mieć każdy hook w osobnym pliku ;) Ja u siebie robię tak :

- root
    - hooks
        - useCośTam.ts
    - components
        - Button
            - hooks
                - useCośButton.ts
            - Button.tsx
            - Button.test.tsx

 

Podobne pytania

+1 głos
1 odpowiedź 287 wizyt
pytanie zadane 27 maja 2021 w JavaScript przez p4wix Obywatel (1,040 p.)
0 głosów
1 odpowiedź 605 wizyt
pytanie zadane 29 grudnia 2020 w JavaScript przez sKodowany Obywatel (1,150 p.)
0 głosów
1 odpowiedź 630 wizyt

93,778 zapytań

142,737 odpowiedzi

323,390 komentarzy

63,382 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj

Twierdza Linux. Bezpieczeństwo dla dociekliwych

Aby uzyskać rabat -10%, użyjcie kodu pasja-linux, wpisując go w specjalne pole w koszyku.

...