curgere este un verificator de tip static pentru Javascript. Această postare este destinată celor care au auzit de la Flow, dar încă nu am încercat să-l folosim în cadrul unei aplicații React. Dacă este prima dată când auziți de Flow, vă pot recomanda aceste patru postări de Preethi Kasireddy ca o introducere excelentă.

Un lucru grozav despre Flow este că este posibil să îl utilizați în mod incremental. Nu trebuie să refactorizați complet un proiect existent pentru a începe să îl utilizați. Poate fi adăugat doar la fișierele noi sau poate fi încercat încet în fișierele existente pentru a vedea dacă oferă beneficii proiectului dvs. specific înainte de a se angaja complet.

Deoarece configurarea unui nou instrument poate fi adesea cea mai provocatoare, în acest post vom lua un proiect existent și vom parcurge configurarea adăugării Flow. O introducere generală a sintaxei este tratată în a doua dintre postările lui Preethi și în Documente de flux sunt, de asemenea, foarte lizibile.

Vom folosi acest lucru exemplu repo, cu două directoare pentru pre- și post- Flow. Folosește Skyscanner script personalizat Create React App backpack-react-scripts, asociat cu obiceiul lor Componente pentru rucsac. Aceasta vizează crearea de exemple mai complexe decât fragmentele individuale, dar totuși lizibile, chiar dacă nu le cunoașteți.

Natura exactă a aplicației nu este importantă în comparație cu a vedea diferența dintre implementarea sa fără și cu Curgere. Foarte puține fișiere se schimbă aici, dar sunt adesea cele mai frustrante pentru a obține dreptate!

Să parcurgem fiecare pas și apoi să aruncăm o privire la convertirea componentelor de exemplu.

Instalați principalele dependențe

Alături de Flow în sine, instalați babel-cli și babel-preset-flow, astfel încât babel să poată elimina adnotările de tip la compilare.

npm install flow-bin babel-cli babel-preset-flow --save-dev

Configurați Babel

Pentru ca acestea să aibă efect creați un .babelrc sau adăugați la fișierul existent .babelrc urmând config:

{
  "presets": ["flow"]
}

Scripturi de configurare

Dacă folosiți cârlige, cum ar fi un script de testare, vă recomandăm să le actualizați și să adăugați Fluxul de bază scenariu pentru dumneavoastră package.json:

"scripts": {
  "flow": "flow",
  "pretest": "npm run flow && npm run lint"
}

Generați un fluxconfig

Dacă rulați fluxul pentru prima dată, puteți genera un șablon .flowconfig prin alergare npm run flow init. În exemplul nostru ne putem vedea extinde-l pentru a adăuga următoarele:

Ignorați tiparele

Pentru a evita fluxul analiza modulelor nodului dvs. și a construi ieșirea, acestea pot fi ușor ignorate.

[ignore].*/node_modules/*.*/build/*

Adăugați suport pentru module CSS

Dacă utilizați module CSS, tipul acestora trebuie specificat pentru ca Flow să le înțeleagă, altfel veți primi această eroare:

Cum se adauga in mod incremental Flow intr o aplicatie
Eroare: [flow] Nu se poate rezolva modulul `CSSModule`.

Acest lucru se face în doi pași. Mai întâi, se adaugă cele de mai jos .flowconfig:

[libs]
./src/types/global.js  // this can be any path and filename you wish
[options]
module.name_mapper="^(.*).scss$" -> 'CSSModule'
module.system=haste

Și în al doilea rând se creează un tip de modul CSS în fișierul menționat în [libs].

// @flow
declare module CSSModule {
  declare var exports: { [key: string]: string };
  declare export default typeof exports;
}

Sincronizați cu alte lintere utilizate

În exemplul de proiect, ESLint este deja utilizat pentru a furniza scame standard. Există câțiva pași de configurare inițiali necesari pentru ca ESLint să se joace frumos cu Flow și alții mai târziu datorită tipurilor specifice utilizate în acest proiect.

Pentru configurarea generală, următorul este adăugat la noi .eslintrc:

"extends": [
  "plugin:flowtype/recommended"
],
"plugins": [
  "flowtype"
]

Extensiile specifice acestui exemplu și erorile pe care le evită vor fi acoperite până la sfârșitul acestei postări.

Flux tastat libdefs

Piesa finală de configurare este să vă pregătiți pentru utilizare libdefs creat folosind flow-typed Pachet NPM. Aceasta este utilizată pentru a crea definiții pentru modulele de nod instalate și implicit creează aceste fișiere într-un flow-typed/ director.

Noi do doriți să comiteți acest fișier, dar nu doriți ca ESLint să-l scape. Acest lucru creează o problemă, așa cum a fost anterior scriptul nostru de scame în package.json este setat să ne folosească .gitignore să știți în timp ce fișierele ESLint ar trebui să ignore, de asemenea:

"lint:js": "eslint . --ignore-path .gitignore --ext .js,.jsx",

Acum vrem să schimbăm acest lucru, deoarece vrem ca ESLint să ignore și ceea ce trebuie creat flow-typed/ director. Ne putem modifica scriptul pentru:

"lint:js": "eslint . --ext .js,.jsx",

Aceasta înseamnă că acum va reveni la utilizarea unui .eslintignore fișier, deci trebuie să creăm acest lucru, să duplicăm ceea ce este în .gitignore, și adăugați directorul suplimentar de ignorat la ea.

În cele din urmă, trebuie să instalăm flow-types. Facem acest lucru la nivel global.

npm install flow-typed -g

libdefs pot fi definiții complete sau stuburi care acceptă orice tip. O lista de definiții complete este menținut. Pentru a vedea dacă există unul disponibil pentru un pachet pe care îl utilizați

flow-typed install my-dependency@<version.being.used>

și acest lucru îl va adăuga fie la dvs. flow-typed sau vă solicită să creați un cod folosind

flow-typed create-stub my-dependency@<version.being.used>

Dacă doriți să creați o definiție completă, puteți face acest lucru și, de asemenea, să o contribuiți din nou la depozit, astfel încât să fie disponibilă pentru alți dezvoltatori.

Un proces simplu de urmat este doar crearea libdefs deoarece sunt cerute în mod specific. Pentru fiecare componentă pe care o convertiți pentru a utiliza Flow adăugați importurile sale folosind flow-typed în acel moment, nu este necesar să adăugați tipuri pentru toate dependențele dacă nu sunt utilizate în fișiere în care Fluxul este, de asemenea, utilizat.

Conversia componentelor existente

Aceasta este toată configurarea generală realizată, acum ne putem uita la convertirea componentelor noastre de exemplu!

Avem două, o componentă de stare și una de funcție. În general, acestea creează un banner decât are un text și un buton. Pe textul de pe banner se poate face clic pentru a deschide un popover, care conține o listă cu glonț.

1611947051 46 Cum se adauga in mod incremental Flow intr o aplicatie
Banner cu un buton de închidere și un popover de informații

Adăugați definiții tip flux

Pentru orice componentă, primul pas este crearea flow-typed definiții pentru orice import din componenta la care lucrăm.

De exemplu, dacă am avea doar importuri de

import React from 'react';
import BpkButton from 'bpk-component-button';

atunci am încerca:

flow-typed install bpk-component-button@<its.installed.versipe>

dacă nu ar fi disponibil și în prezent nu este, atunci am defini definiția sa:

flow-typed create-stub bpk-component-button@latest

În exemplul repo putem vedea lista tuturor definițiilor create pentru componentele la care ne-am mutat folosind Flow. Acestea au fost adăugate pe rând, deoarece fiecare componentă avea Flow integrat cu ele.

Componente funcționale

În exemplul nostru fără Flux folosim PropTypes pentru unele verificări de tip limitate și capacitatea lor de a defini defaultProps pentru utilizare în dezvoltare.

Poate arăta puțin complex la prima vedere, dar este relativ puțin ce trebuie să schimbăm pentru a adăuga Flow.

1611947051 33 Cum se adauga in mod incremental Flow intr o aplicatie
Componentă înainte de a adăuga Flow

Pentru a transforma acest lucru pentru a utiliza Flow, putem elimina mai întâi PropTypes import și definiții. // @flow adnotarea poate fi apoi adăugată la prima linie.

Pentru această componentă vom tasta doar verificarea accesoriilor transmise. Pentru a face acest lucru, vom crea mai întâi un tip Recuzită, mult mai curat decât definirea fiecărui accesoriu în linie.

type Props = {
  strings: { [string_key: string]: string },
  onClose: Function,
  isOpen: boolean,
  target: Function,
};

Aici ultimele trei tipuri se explică de la sine. La fel de strings este un obiect de corzi an obiect ca hartă a fost folosit, verificând fiecare cheie și valoare din obiectul primit pentru a verifica dacă tipurile lor se potrivesc, fără a fi nevoie să specificați exact cheile lor de șir.

Definițiile tipurilor de prop pot fi apoi eliminate împreună cu importul său. Deoarece defaultProps nu sunt legate de acest import, ele pot și ar trebui să rămână. * Consultați comentariile ESLint de închidere pentru orice erori raportate în acest moment.

Componenta ar trebui să arate acum astfel:

1611947052 838 Cum se adauga in mod incremental Flow intr o aplicatie
Componentă după adăugarea Flow

Componente de stare

Componentele de stare urmează câteva declarații ușor diferite. Deoarece această componentă este mai complexă, ne vom uita și la declararea tipurilor pentru unele aspecte suplimentare.

Ca și înainte, aruncă mai întâi o privire componentă înainte de a adăuga Flow.

Recuzită și stat

Ca și în componenta funcțională, eliminăm mai întâi propTypes definiție și import și adăugați fișierul // @flow adnotare.

Mai întâi vom arunca o privire asupra adăugării de tipuri pentru Accesorii și State. Din nou, vom crea tipuri pentru acestea:

type Props = {
  strings: { [string_key: string]: string },
  hideBannerClick: Function,
}; 
type State = {
  popoverIsOpen: boolean,
};

și specificați că componenta le va utiliza:

class Banner extends Component<Props, State> {
  constructor(props: Props) {
    super(props);    
    this.state = {
      popoverIsOpen: false,
    };
  ...
  };
...
};

Apoi am lovit prima noastră diferență între componentele funcționale și stateful, defaultProps. Într-o componentă Function, acestea au fost declarate așa cum suntem obișnuiți, în componentele Stateful externe Banner.defaultProps sintaxa este eliminată și, în schimb, valorile implicite sunt declarate în cadrul clasei:

class Banner extends Component<Props, State> {
  static defaultProps = {
    strings: defaultStrings,
  };
constructor(props: Props) {
...
// the below is removed
// Banner.defaultProps = {
//  strings: defaultStrings,
// };

Declarațiile constructorului

stringWithPlaceholder este declarat în cadrul constructorului. Aici nu ne uităm De ce este declarat acolo (vom presupune că există motive întemeiate), ci mai degrabă pentru a vedea dacă fluxul poate fi adăugat fără modificări la codul existent.

Dacă vom rula în starea sa existentă, vom întâlni eroarea Cannot get this.stringWithPlaceholder because property stringWithPlaceholder is missing in Banner [1].

Pentru a remedia acest lucru, trebuie să adăugăm o singură linie în interiorul blocului clasei Banner, chiar sub și în afara constructorului:

class Banner extends Component<Props, State> {
  constructor(props: Props) {
    super(props);    
    this.state = {
      popoverIsOpen: false,
    };
    this.stringWithPlaceholder = ...
  };
  stringWithPlaceholder: string;
...
};

Această variabilă este creată în constructor, dar nu este transmisă ca elemente de recuzită. Deoarece folosim Flow pentru verificarea de tip a elementelor de recuzită trecute în constructor, aceasta necesită totul în interiorul constructorului fi tip verificat. Este cunoscut că Flow necesită acest lucru, iar acest lucru se poate face prin specificarea tipului lor în blocul de clase.

În acest moment, accesoriile și statul sunt complete. Să vedem câteva exemple suplimentare rapide de verificare a tipului în cadrul acestei componente. * Consultați comentariile ESLint de închidere pentru orice erori raportate în acest moment.

Tipuri Return, Event și Node

togglePopover nu ia niciun argument, deci poate fi văzut un exemplu simplu de specificare a niciunei valori de returnare:

togglePopover = (): void => {
  ...
};

keyboardOnlyTogglePopover nu returnează nimic, dar are un singur parametru. Acesta este un eveniment, în special un eveniment de apăsare a tastelor. SyntheticKeyboardEvent este folosit la fel de

React folosește propriul sistem de evenimente, deci este important să utilizați tipurile SyntheticEvent în loc de tipurile DOM precum Event, KeyboardEvent și MouseEvent.

keyboardOnlyTogglePopover = (e: SyntheticKeyboardEvent<>): void => {
  ...
};

Popover este definit în render() și returnează o instanță a ListPopover Componentă funcțională pe care am analizat-o anterior. Putem specifica tipul său de returnare ca React Node. Cu toate acestea, pentru a putea face acest lucru, trebuie mai întâi să-l importăm, așa cum este nu este accesibil în mod implicit. Există mai multe modalități de a-l importa, dintre care unul indicat mai jos:

import React, { Component } from 'react';
import type { Node } from 'react';
...
const Popover: Node = (
  <ListPopover
    onClose={this.togglePopover}
    isOpen={this.state.popoverIsOpen}
    strings={this.props.strings}
    target={() => document.getElementById('ListPopoverLink')}
  />
);

Verificarea tipului componentelor React importate

Când tipurile Prop au fost declarate într-o componentă, ele pot fi folosite atunci când se utilizează componenta respectivă într-o altă componentă. Cu toate acestea, dacă utilizați un index.js pentru a exporta prima componentă, apoi fluxul, // @flow va trebui adăugat la index.

De exemplu:

// @flow
import ListPopover from './ListPopover';
export default ListPopover;

Marcarea elementelor de recuzită ca opțional

Un accesoriu poate fi marcat ca opțional folosind prop?: type sintaxă, de exemplu:

type Props = {  
  strings: { [string_key: string]: string },  
  hideBannerClick?: Function,
};

Acest lucru este acceptat, dar nu mai este recomandat de Flow. În schimb, toate accesoriile ar trebui lăsate după cum este necesar, cu nr ? , chiar dacă este opțional, ca Flow detectează automat default Propune și marchează recuzita cu o valoare implicită ca opțională la nivel intern.

În secțiunea de mai jos putem vedea cum marcarea manuală a accesoriilor ca opțional poate provoca conflicte cu alte instrumente în unele cazuri.

Extensii ESLint, accesorii implicite și soluții de eroare de validare a accesoriilor

Două adăugiri sunt făcute la .eslintrc. Pentru acest proiect, puteți accepta pur și simplu utilizarea lor sau puteți citi detaliile de mai jos dacă vedeți oricare dintre cele trei erori:

  • x missing in props validation
  • error defaultProp "x" defined for isRequired propType
  • Cannot get strings.xxx because property xxx is missing in undefined

Regulile adăugate, cu raționament, sunt:

"react/default-props-match-prop-types": [
  "error", { "allowRequiredDefaults": true }
]

Atunci când se utilizează obiecte ca hărți (în acest caz pentru prop-ul „șiruri”) a missing in props validation apare o eroare. Aceasta este un gândac și așa este în mod explicit ignorat Aici.

"react/default-props-match-prop-types": [  "error", { "allowRequiredDefaults": true }]

Când folosiți obiecte ca hărți, intră în joc complexități între ESLint, flux și tipuri de prop.

strings este un element necesar, transmis ca obiect al șirurilor. Tipul de flux verifică dacă pentru fiecare intrare din obiect cheia șirului este un șir, iar valoarea este un șir. Acest lucru este mult mai ușor de întreținut decât să fie nevoie să enumerați tipul de accesoriu al fiecărei chei specifice.

Dacă recuzita este marcată după cum este necesar în Flow, atunci ESLint ar erora afirmând: error defaultProp "strings" defined for isRequired propType.

Dacă recuzita este marcată manual ca opțională, atunci Flow va erora cu Cannot get strings.xxx because property xxx is missing in undefined [1].

Aceasta este cunoscut și se datorează invalidarea rafinamentului deoarece JSX poate transforma apelurile de metodă, Flow nu poate fi sigur că xxx nu a fost redefinit.

Acest lucru ne lasă cu remedierea erorii ESLint. Regulile de mai sus permit definirea DefaultProps în timp ce tipul Flow este nu marcat ca opțional. Flow va înțelege acest lucru și îl va converti în opțional. ESLint este marcat cu "allowRequiredDefaults": true, ceea ce înseamnă că, deși ESLint vede recuzita ca fiind necesară, nu va greși.

Gânduri finale

Odată depășit obstacolul inițial de instalare, Flow este destul de simplu de utilizat. Abilitatea de a-l adăuga progresiv ajută cu siguranță, mai degrabă decât să fie nevoie să refactorizați un întreg proiect dintr-o singură dată.

Sperăm că instrucțiunile de configurare și exemplele de aici se dovedesc utile dacă doriți să încercați Flow out.

Mulțumesc că ai citit?

Vă puteți bucura, de asemenea: