Funkcje w składnikach bezstanowych?


90

Próbuję przekonwertować tę fajną <canvas>animację, którą tu znalazłem , na komponent wielokrotnego użytku React. Wygląda na to, że ten komponent wymagałby jednego komponentu nadrzędnego dla kanwy i wielu komponentów podrzędnych dla function Ball().

Ze względu na wydajność prawdopodobnie lepiej byłoby uczynić z nich Ballskomponenty bezstanowe, ponieważ będzie ich wiele. Nie jestem zaznajomiony z tworzeniem komponentów bezstanowych i zastanawiałem się, gdzie powinienem zdefiniować funkcje this.update()i, this.drawktóre są zdefiniowane w programie function Ball().

Czy funkcje dla komponentów bezstanowych znajdują się wewnątrz komponentu czy na zewnątrz? Innymi słowy, które z poniższych jest lepsze?

1:

const Ball = (props) => {
    const update = () => {
        ...
    }

    const draw = () => {
        ...
    }

    return (
       ...
    );
}

2:

function update() {
     ...
}

function draw() {
     ...
}

const Ball = (props) => {
    return (
       ...
    );
}

Jakie są zalety / wady każdego z nich i czy jeden z nich jest lepszy w konkretnych przypadkach użycia, takich jak mój?


Czy możesz opublikować istniejący kod, abyśmy mogli zobaczyć, jak zostanie wykorzystany?
Scimonster,

@Scimonster Opublikowałem to w osadzonym linku, może to przegapiłeś. Oto link: codepen.io/awendland/pen/XJExGv
MarksCode

Odpowiedzi:


113

Pierwszą rzeczą, na którą należy zwrócić uwagę, jest to, że bezstanowe komponenty funkcjonalne nie mogą mieć metod, nie powinieneś liczyć na wywołanie updatelub drawrenderowanie, Balljeśli jest to bezstanowy składnik funkcjonalny.

W większości przypadków powinieneś zadeklarować funkcje poza funkcją komponentu, więc deklarujesz je tylko raz i zawsze ponownie używasz tego samego odniesienia. Kiedy deklarujesz funkcję wewnątrz, za każdym razem, gdy komponent jest renderowany, funkcja zostanie ponownie zdefiniowana.

Istnieją przypadki, w których będziesz musiał zdefiniować funkcję wewnątrz komponentu, aby na przykład przypisać ją jako procedurę obsługi zdarzeń, która zachowuje się inaczej w zależności od właściwości komponentu. Ale nadal możesz zdefiniować funkcję na zewnątrz Balli powiązać ją z właściwościami, dzięki czemu kod będzie znacznie bardziej przejrzysty i sprawi, że funkcje updatelub będą drawwielokrotnego użytku.

// You can use update somewhere else
const update (propX, a, b) => { ... };

const Ball = props => (
  <Something onClick={update.bind(null, props.x)} />
);

Jeśli używasz hooków , możesz użyć, useCallbackaby upewnić się, że funkcja zostanie przedefiniowana tylko wtedy, gdy jedna z jej zależności ( props.xw tym przypadku) ulegnie zmianie:

const Ball = props => {
  const onClick = useCallback((a, b) => {
    // do something with a, b and props.x
  }, [props.x]);

  return (
    <Something onClick={onClick} />
  );
}

To jest zły sposób :

const Ball = props => {
  function update(a, b) {
    // props.x is visible here
  }

  return (
    <Something onClick={update} />
  );
}

Podczas używania useCallbackzdefiniowanie updatefunkcji w samym useCallbackhaku poza komponentem staje się bardziej decyzją projektową, niż cokolwiek innego, powinieneś wziąć pod uwagę, czy zamierzasz ponownie użyć updatei / lub jeśli potrzebujesz dostępu do zakresu zamknięcia komponentu, na przykład odczyt / zapis stanu. Osobiście wybieram zdefiniowanie go domyślnie wewnątrz komponentu i umożliwiam jego ponowne użycie tylko wtedy, gdy zajdzie taka potrzeba, aby zapobiec przeprojektowaniu od samego początku. Poza tym ponowne wykorzystanie logiki aplikacji jest lepsze dzięki bardziej szczegółowym punktom zaczepienia, pozostawiając komponenty do celów prezentacyjnych. Definiowanie funkcji poza komponentem podczas używania hooków zależy tak naprawdę od stopnia oddzielenia od Reacta, jakiego chcesz dla logiki aplikacji.


Dzięki Marco, to trochę wyjaśnia sprawę. W moim przypadku jestem zdezorientowany, jest związany z this.drawfunkcją wewnątrz Ball. Używa ctxz tego, co byłoby rodzica, <canvas>a także używa thissłowa kluczowego dla tego, co byłoby Ballskładnikiem potomnym . Jaki byłby najlepszy sposób na zintegrowanie implementacji składnika bezstanowego, tak aby obie te właściwości były dostępne?
MarksCode,

nie ma takiej thissytuacji w przypadku używania bezstanowych komponentów funkcjonalnych, miej to na uwadze. W kontekście płótna musiałbyś przekazać to każdemu Ball, to wcale nie brzmi dobrze.
Marco Scabbiolo

Więc w tym przypadku najlepiej byłoby po prostu nie mieć komponentu potomnego, o którym mówisz?
MarksCode,

1
@MarcoScabbiolo no no, to nie mój przypadek, już od dłuższego czasu natywnie używam funkcji strzałek, ponieważ jedyną przeglądarką, która ich nie obsługuje, jest IE. Właściwie udało mi się znaleźć ten komentarz z jednego artykułu, w którym faktycznie twierdzono, że bindw Chrome wcześniej niż 59 był nawet wolniejszy niż funkcje strzałkowe. W Firefoksie jest to również sporo czasu, ponieważ oba działają z taką samą prędkością. Więc powiedziałbym, że w takich przypadkach nie ma żadnej różnicy, co jest preferowanym sposobem :)
Vadim Kalinin

1
@ MauricioAvendaño w obu przypadkach działa, ale Somethingwiedza o tym, że komponent nadrzędny zawiera właściwość X , jest złą praktyką, ponieważ uświadamia mu kontekst. To samo dzieje się z pytaniem, które zadajesz, i przykładowym kodem, który napisałem, zależy to od kontekstu, który jest ignorowany ze względu na prostotę.
Marco Scabbiolo

12

Możemy mieć funkcje wewnątrz bezstanowych komponentów funkcjonalnych, poniżej znajduje się przykład:

 const Action = () => {
  function  handlePick(){
     alert("test");
  }
  return (
    <div>
      <input type="button" onClick={handlePick} value="What you want to do ?" />
    </div>
  );
}

Nie jest to jednak dobra praktyka, ponieważ funkcja handlePick()będzie definiowana za każdym razem, gdy zostanie wywołany komponent.


11
Jeśli to nie jest dobra praktyka, jakie będzie alternatywne rozwiązanie?
John Samuel

@JohnSamuel Alternatywą jest zdefiniowanie handlePick()powyżej / poza komponentem, function handlePick() { ... }; const Action = () => { ... }w wyniku czego handlePick jest zdefiniowany tylko raz. Jeśli potrzebujesz danych z Actionkomponentu, powinieneś przekazać je jako parametry do handlePickfunkcji.
James Hay

11
jeśli to nie jest dobra praktyka, możesz dodać dobrą praktykę do odpowiedzi? teraz muszę ponownie poszukać dobrej praktyki :(
Shamseer Ahammed,

2

Możemy użyć haka React useCallbackjak poniżej w komponencie funkcjonalnym:

const home = (props) => {
    const { small, img } = props
    const [currentInd, setCurrentInd] = useState(0);
    const imgArrayLength = img.length - 1;
    useEffect(() => {
        let id = setInterval(() => {
            if (currentInd < imgArrayLength) {
                setCurrentInd(currentInd => currentInd + 1)
            }
            else {
                setCurrentInd(0)
            }
        }, 5000);
        return () => clearInterval(id);
    }, [currentInd]);
    const onLeftClickHandler = useCallback(
        () => {
            if (currentInd === 0) {

            }
            else {
                setCurrentInd(currentInd => currentInd - 1)
            }
        },
        [currentInd],
    );

    const onRightClickHandler = useCallback(
        () => {
            if (currentInd < imgArrayLength) {
                setCurrentInd(currentInd => currentInd + 1)
            }
            else {

            }
        },
        [currentInd],
    );
    return (
        <Wrapper img={img[currentInd]}>
            <LeftSliderArrow className={currentInd > 0 ? "red" : 'no-red'} onClick={onLeftClickHandler}>
                <img src={Icon_dir + "chevron_left_light.png"}></img>
            </LeftSliderArrow>
            <RightSliderArrow className={currentInd < imgArrayLength ? "red" : 'no-red'} onClick={onRightClickHandler}>
                <img src={Icon_dir + "chevron_right_light.png"}></img>
            </RightSliderArrow>
        </Wrapper>);
}

export default home;

Otrzymuję „img” od jego rodzica i to jest tablica.


0

Jeśli chcesz użyć właściwości lub stanu komponentu w funkcji, należy to zdefiniować w komponencie za pomocą useCallback.

function Component(props){
  const onClick=useCallback(()=>{
     // Do some things with props or state
  },[])
}

Z drugiej strony, jeśli nie chcesz używać właściwości lub stanu w funkcji, zdefiniuj to poza komponentem.

const computeSomethings=()=>{
   // Do some things with params or side effects
}

function Component(props){
  
}

3
czy możesz podać przykład użycia haka do metody wewnątrz elementu funkcjonalnego? (metoda
jake-ferguson
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.