Schreiben einer API für React-Komponenten, Teil 3: Die Reihenfolge der Requisiten ist wichtig

Schreiben einer API für React-Komponenten, Teil 1: Erstellen Sie keine widersprüchlichen Requisiten

Schreiben einer API für Reaktionskomponenten, Teil 2: Geben Sie dem Verhalten Namen, nicht der Interaktion

Schreiben einer API für React-Komponenten, Teil 3: Die Reihenfolge der Requisiten ist wichtig

Schreiben einer API für React Components, Teil 4: Vorsicht vor der Apropacalypse!

Schreiben einer API für Reaktionskomponenten, Teil 5: Verwenden Sie einfach die Komposition

Wir schreiben API für React-Komponenten, Teil 6: Wir erstellen die Kommunikation zwischen Komponenten

Beginnen wir mit einer einfachen React-Komponente, die ein Ankertag anzeigt:


Link


<Link href="sid.studio">Click me</Link> //   : <a href="sid.studio" class="link">Click me</a> 

So sieht der Komponentencode aus:


 const Link = props => { return ( <a href={props.href} className="link"> {props.children} </a> ) } 

Wir möchten auch in der Lage sein, dem Element HTML-Attribute wie id , target , title , data-attr usw. data-attr .


Da es viele HTML-Attribute gibt, können wir einfach alle Requisiten übergeben und die benötigten hinzufügen (wie className ).


(Hinweis: Sie sollten die Attribute, die Sie für diese Komponente erstellt haben und die nicht in der HTML-Spezifikation enthalten sind, nicht übergeben.)


In diesem Fall können Sie einfach className


 const Link = props => { /*    (spread ),     ( ) */ return <a {...props} className="link" /> } 

Dort wird es interessant.


Alles scheint in Ordnung zu sein, wenn jemand eine id oder ein target übergibt:


 <Link href="sid.studio" id="my-link">Click me</Link> //   : <a href="sid.studio" id="my-link" class="link">Click me</a> 

aber was passiert, wenn jemand className ?


Link


 <Link href="sid.studio" className="red-link">Click me</Link> //   : <a href="sid.studio" class="link">Click me</a> 

Nun, nichts ist passiert. React ignorierte die benutzerdefinierte Klasse vollständig. Kehren wir zur Funktion zurück:


 const Link = props => { return <a {...props} className="link" /> } 

Ok, stellen wir uns vor, wie diese ...props kompiliert werden. Der obige Code entspricht dem:


 const Link = props => { return ( <a href="sid.studio" className="red-link" className="link" > Click me </a> ) } 

Sehen Sie den Konflikt? Es gibt zwei className . Wie geht React damit um?


Nun, React macht nichts. Babel tut es!


Denken Sie daran, dass JSX React.createElement "erzeugt". Requisiten werden in ein Objekt konvertiert und als Argument übergeben. Objekte unterstützen keine doppelten Schlüssel, daher überschreibt der zweite className den ersten.


 const Link = props => { return React.createElement( 'a', { className: 'link', href: 'sid.studio' }, 'Click me' ) } 



Okay, jetzt, da wir über das Problem Bescheid wissen, wie können wir es lösen?


Es ist hilfreich zu verstehen, dass dieser Fehler aufgrund eines Namenskonflikts aufgetreten ist. Dies kann bei jeder Requisite und nicht nur bei className . Die Entscheidung hängt also vom Verhalten ab, das Sie implementieren möchten.


Es gibt drei mögliche Szenarien:


  1. Ein Entwickler, der unsere Komponente verwendet, sollte in der Lage sein, den Standard-Requisitenwert zu überschreiben.
  2. Wir möchten nicht, dass der Entwickler einige Requisiten ändert
  3. Der Entwickler sollte in der Lage sein, Werte hinzuzufügen, während der Standardwert beibehalten wird.

Nehmen wir sie einzeln auseinander.


1. Der Entwickler, der unsere Komponente verwendet, sollte in der Lage sein, den Standard-Requisitenwert zu überschreiben


Dies ist das Verhalten, das Sie normalerweise von anderen Attributen erwarten - id , title usw.


Wir sehen oft die test id Einstellung im Kosmos (dem Design-System, an dem ich arbeite). Jede Komponente erhält eine Standard data-test-id . Manchmal möchten Entwickler stattdessen ihre eigene Testkennung anhängen, um eine bestimmte Verwendung anzugeben.


Hier ist ein solcher Anwendungsfall:


Semmelbrösel


 const Breadcrumb = () => ( <div className="breadcrumb" data-test-id="breadcrumb"> <Link data-test-id="breadcrumb.link">Home</Link> <Link data-test-id="breadcrumb.link">Parent</Link> <Link data-test-id="breadcrumb.link">Page</Link> </div> ) 

Breadcrumb verwendet den Link, aber Sie möchten ihn in Tests mit einer spezifischeren data-test-id . Es ist ein Fehler darin.


In den meisten Fällen sollten benutzerdefinierte Requisiten Vorrang vor Standard-Requisiten haben.


In der Praxis bedeutet dies, dass Standard-Requisiten zuerst verwendet werden sollten und dann {...props} , um sie zu überschreiben.


 const Link = props => { return ( <a className="link" data-test-id="link" {...props} /> ) } 

Denken Sie daran, dass das zweite Erscheinungsbild der data-test-id (von Requisiten) das erste (standardmäßig) überschreibt. Wenn ein Entwickler seine eigene className data-test-id oder seinen eigenen className , überschreibt er daher die standardmäßig folgende:


 1. <Link href="sid.studio">Click me</Link> 2. <Link href="sid.studio" data-test-id="breadcrumb.link">Click me</Link> //   : 1. <a class="link" href="sid.studio" data-test-id="link">Click me</a> 2. <a class="link" href="sid.studio" data-test-id="breadcrumb.link">Click me</a> 

Wir können dasselbe mit className :


Red-Link


 <Link href="sid.studio" className="red-link">Click me</Link> //   : <a href="sid.studio" class="red-link" data-test-id="link">Click me</a> 

Es sieht ziemlich seltsam aus, ich bin mir nicht sicher, ob wir das zulassen sollten! Lassen Sie uns weiter darüber sprechen.


2. Wir möchten nicht, dass der Entwickler einige Requisiten ändert


Angenommen, wir möchten nicht, dass Entwickler das Erscheinungsbild (über className ) className , aber es macht uns nichts aus, dass sie andere Requisiten wie id , className data-test-id usw. ändern.


Wir können dies tun, indem wir die Reihenfolge unserer Attribute bestellen:


 const Link = props => { return ( <a data-test-id="link" {...props} className="link" /> ) } 

Denken Sie daran, dass das Attribut rechts das Attribut links überschreibt. Somit kann alles vor {...props} neu definiert werden, aber alles danach kann nicht neu definiert werden.


Um die Arbeit der Entwickler zu vereinfachen, können Sie eine Warnung anzeigen, dass Sie Ihren className nicht angeben können.


Ich erstelle gerne meine eigenen Requisiten für die Typprüfung:


 Link.PropTypes = { className: function(props) { if (props.className) { return new Error( `  className  Link,      ` ) } } } 

Ich habe ein Video, in dem es darum geht, benutzerdefinierte Arten von Requisiten zu überprüfen , falls Sie daran interessiert sind, wie man sie schreibt.


Wenn der Entwickler nun versucht, className zu überschreiben, className dies nicht und der Entwickler erhält eine Warnung.


Link


 <Link href="sid.studio" className="red-link">Click me</Link> //   : <a href="sid.studio" class="link">Click me</a> 

 :   :   className  Link,       

Ehrlich gesagt musste ich diese Vorlage nur ein- oder zweimal verwenden. Normalerweise vertrauen Sie dem Entwickler, der Ihre Komponente verwendet.


Was uns zum Teilen bringt.


3. Der Entwickler sollte in der Lage sein, Werte hinzuzufügen, während der Standardwert beibehalten wird


Dies ist möglicherweise der häufigste Anwendungsfall für Klassen.


Link


 <Link href="sid.studio" className="underline">Click me</Link> //   : <a href="sid.studio" class="link underline">Click me</a> 

Die Implementierung sieht folgendermaßen aus:


 const Link = props => { /*  className   */ const { className, otherProps } = props /*     */ const classes = 'link ' + className return ( <a data-test-id="link" className={classes} {...otherProps} /*     */ /> ) } 

Diese Vorlage ist auch nützlich, um Ereignishandler (z. B. onClick ) für eine Komponente zu akzeptieren, die sie bereits hat.


Schalter


 <Switch onClick={value => console.log(value)} /> 

So sieht die Implementierung dieser Komponente aus:


 class Switch extends React.Component { state = { enabled: false } onToggle = event => { /*      */ this.setState({ enabled: !this.state.enabled }) /*       */ if (typeof this.props.onClick === 'function') { this.props.onClick(event, this.state.enabled) } } render() { /*        ️ */ return <div class="toggler" onClick={this.onToggle} /> } } 

Es gibt eine andere Möglichkeit, Namenskonflikte in Ereignishandlern zu vermeiden. Ich habe dies in der Schreib-API für Reaktionskomponenten, Teil 2, beschrieben: Geben Sie dem Verhalten Namen, nicht Interaktionsmöglichkeiten .




Für jedes Szenario können Sie unterschiedliche Ansätze verwenden.


  1. Meistens: Der Entwickler sollte in der Lage sein, den Wert der Requisite zu ändern, dessen Wert standardmäßig festgelegt wurde
  2. Normalerweise für Stile und Ereignishandler: Der Entwickler sollte in der Lage sein, einen Wert über dem Standardwert hinzuzufügen
  3. Ein seltener Fall, wenn Sie die Aktionen des Entwicklers einschränken müssen: Der Entwickler darf das Verhalten nicht ändern, Sie müssen seine Werte ignorieren und gleichzeitig Warnungen anzeigen

Source: https://habr.com/ru/post/de459380/


All Articles