Funktionen in zustandslosen Komponenten?

90

Ich versuche, diese coole <canvas>Animation, die ich hier gefunden habe, in eine wiederverwendbare React-Komponente umzuwandeln . Es sieht so aus, als würde diese Komponente eine übergeordnete Komponente für die Zeichenfläche und viele untergeordnete Komponenten für die Zeichenfläche erfordern function Ball().

Aus Leistungsgründen wäre es wahrscheinlich besser, die Ballszu zustandslosen Komponenten zu machen, da es viele davon geben wird. Ich bin nicht so vertraut mit der Herstellung zustandsloser Komponenten und habe mich gefragt, wo ich die this.update()und this.drawFunktionen definieren soll, die in definiert sind function Ball().

Gehen Funktionen für zustandslose Komponenten innerhalb oder außerhalb der Komponente? Mit anderen Worten, welche der folgenden Möglichkeiten ist besser?

1:

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

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

    return (
       ...
    );
}

2:

function update() {
     ...
}

function draw() {
     ...
}

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

Was sind die Vor- und Nachteile von jedem und ist einer von ihnen besser für bestimmte Anwendungsfälle wie meinen?

MarksCode
quelle
Können Sie den vorhandenen Code veröffentlichen, damit wir sehen, wie er verwendet wird?
Scimonster
@ Scimonster Ich habe es in einem eingebetteten Link gepostet, vielleicht hast du es verpasst. Hier ist der Link: codepen.io/awendland/pen/XJExGv
MarksCode

Antworten:

113

Als erstes ist zu beachten, dass zustandslose Funktionskomponenten keine Methoden haben können. Sie sollten nicht mit einem Aufruf updateoder draweinem gerenderten BallGerät rechnen, wenn es sich um eine zustandslose Funktionskomponente handelt.

In den meisten Fällen sollten Sie die Funktionen außerhalb der Komponentenfunktion deklarieren, damit Sie sie nur einmal deklarieren und immer dieselbe Referenz wiederverwenden. Wenn Sie die Funktion im Inneren deklarieren, wird die Funktion jedes Mal neu definiert, wenn die Komponente gerendert wird.

Es gibt Fälle, in denen Sie eine Funktion innerhalb der Komponente definieren müssen, um sie beispielsweise als Ereignishandler zuzuweisen, der sich basierend auf den Eigenschaften der Komponente unterschiedlich verhält. Trotzdem können Sie die Funktion außerhalb definieren Ballund mit den Eigenschaften verknüpfen, wodurch der Code viel sauberer und die Funktionen updateoder drawwiederverwendbar werden.

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

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

Wenn Sie Hooks verwenden , können Sie useCallbacksicherstellen, dass die Funktion nur neu definiert wird, wenn sich eine ihrer Abhängigkeiten ( props.xin diesem Fall) ändert:

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

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

Das ist der falsche Weg :

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

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

Wenn Sie useCallbackdie updateFunktion im useCallbackHook selbst außerhalb der Komponente definieren, wird dies vor allem zu einer Entwurfsentscheidung. Sie sollten berücksichtigen, ob Sie sie wiederverwenden updateund / oder auf den Umfang des Verschlusses der Komponente zugreifen müssen. Lesen / Schreiben Sie beispielsweise in den Status. Persönlich definiere ich es standardmäßig innerhalb der Komponente und mache es nur dann wiederverwendbar, wenn dies erforderlich ist, um ein Über-Engineering von Anfang an zu verhindern. Darüber hinaus ist die Wiederverwendung von Anwendungslogik besser mit spezifischeren Hooks möglich, sodass Komponenten für Präsentationszwecke übrig bleiben. Das Definieren der Funktion außerhalb der Komponente während der Verwendung von Hooks hängt wirklich vom Grad der Entkopplung von React ab, den Sie für Ihre Anwendungslogik wünschen.

Marco Scabbiolo
quelle
Danke Marco, das klärt die Dinge ein bisschen auf. Die Sache, über die ich in meinem Fall verwirrt bin, hängt mit der this.drawFunktion innerhalb der zusammen Ball. Es verwendet das ctxvon dem, was das übergeordnete Element wäre, <canvas>und verwendet auch das thisSchlüsselwort für das, was die untergeordnete BallKomponente wäre. Was wäre der beste Weg, um die zustandslose Komponente zu integrieren, damit auf beide Eigenschaften zugegriffen werden kann?
MarksCode
thisBei der Verwendung zustandsloser Funktionskomponenten gibt es keine , denken Sie daran. Für den Canvas-Kontext müsste man ihn an jeden einzelnen weitergeben Ball, was überhaupt nicht gut klingt.
Marco Scabbiolo
In diesem Fall ist es also am besten, keine untergeordnete Komponente zu haben, die Sie sagen?
MarksCode
1
@MarcoScabbiolo nein nein, das ist nicht mein Fall, da ich Pfeilfunktionen bereits seit geraumer Zeit nativ verwende, da der einzige Browser, der sie nicht unterstützt, der IE ist. Eigentlich habe ich diesen Kommentar aus einem Artikel gefunden, in dem behauptet wird, dass bindspeziell in Chrome vor 59 sogar langsamer als Pfeilfunktionen waren. Und in Firefox ist es auch eine ganze Weile her, da beide mit der gleichen Geschwindigkeit arbeiten. Also würde ich sagen, in solchen Fällen gibt es keinen Unterschied, was der bevorzugte Weg ist :)
Vadim Kalinin
1
@ MauricioAvendaño funktioniert so oder so, aber es ist eine schlechte Praxis für die SomethingKomponente zu wissen, dass sich in ihrer übergeordneten Komponente ein Prop X befindet, da sie auf ihren Kontext aufmerksam macht. Dasselbe gilt für die Frage, die Sie stellen, und für den Beispielcode, den ich geschrieben habe. Dies hängt vom Kontext ab, der der Einfachheit halber ignoriert wird.
Marco Scabbiolo
12

Wir können Funktionen in zustandslosen Funktionskomponenten haben. Das folgende Beispiel ist:

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

Dies ist jedoch keine gute Vorgehensweise, da die Funktion handlePick()jedes Mal definiert wird, wenn die Komponente aufgerufen wird.

Ruchir Saxena
quelle
11
Wenn es keine gute Praxis ist, was wird dann die alternative Lösung sein?
John Samuel
@JohnSamuel Die Alternative besteht darin, handlePick()über / außerhalb der Komponente zu definieren function handlePick() { ... }; const Action = () => { ... }, so dass das Ergebnis ist, dass handlePick nur einmal definiert wird. Wenn Sie Daten von der ActionKomponente benötigen , sollten Sie diese als Parameter an die handlePickFunktion übergeben.
James Hay
12
Wenn dies keine gute Praxis ist, hätten Sie die gute Praxis mit der Antwort hinzufügen können? Jetzt muss ich noch einmal nach der guten Praxis suchen :(
Shamseer Ahammed
2

Wir können den React-Hook useCallbackwie folgt in einer Funktionskomponente verwenden:

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;

Ich bekomme 'img' von seinem Elternteil und das ist ein Array.

Vishant Rastogi
quelle
0

Wenn Sie Requisiten oder den Status der Komponente in der Funktion verwenden möchten, sollte dies in der Komponente mit useCallback definiert werden.

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

Wenn Sie dagegen keine Requisiten oder Statusfunktionen verwenden möchten, definieren Sie diese außerhalb der Komponente.

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

function Component(props){
  
}
Hossein Mohammadi
quelle
3
Können Sie ein Beispiel für die Verwendung von Hook als Methode innerhalb der Funktionskomponente geben? (nicht festgelegte Zustandsmethode)
Jake-Ferguson