Applicazione ReactJS: resilienza VS non veloce

Sono nel bel mezzo dello sviluppo di un’applicazione React e questo è l’approccio che ho usato per i miei componenti: convalido gli oggetti di scena che mi aspetto di ricevere utilizzando la convalida PropTypes ma assegno ancora i valori di default per evitare che esso si interrompe se qualcosa va storto con i dati ricevuti.

Recentemente mi è stato detto che non dovremmo farlo, che gli oggetti di scena sono ciò che ci aspettiamo dal genitore e se il contratto non viene rispettato per lasciare che il componente si rompa.

Quale approccio è corretto e quali sono i pro e i contro?

Alcune delle mie considerazioni come spunti di riflessione ..

Seguendo il mio approccio iniziale, nei test testiamo esplicitamente i valori predefiniti che passano al componente in prova alcuni dati non validi e mi aspetto di stampare un’istantanea valida. I test non falliscono a causa di alcuni dati errati ma stampo gli avvisi di convalida PropTypes (che potrebbero però essere trasformati in errori se lo si desidera – penso – o essere messi a tacere prendendoli in giro nel test).

Questi avvertimenti sia nei test sia nella vera applicazione sono più concisi e chiari del semplice vedere un errore che dice “imansible leggere ‘someProp’ da undefined” o simili (e lasciare che il ciclo di rendering React si interrompa). Le convalide propType ti dicono direttamente e chiaramente cosa hai sbagliato (hai passato il tipo sbagliato come puntello, il puntello mancava completamente, ecc.).

Utilizzando il secondo approccio, invece, i test falliscono perché l’app si interrompe. Penso che questo sia un buon approccio solo se la copertura del test è veramente buona (90/100%) altrimenti è un rischio – potrebbe andare in diretta e rompere i casi limite rovinando la reputazione del prodotto. Le modifiche al refactoring o ai requisiti avvengono abbastanza spesso e alcuni casi limite potrebbero finire con dati indesiderati che interrompono l’applicazione e non sono stati acquisiti in test automatici o manuali.

Ciò significa che quando l’applicazione è triggers, il codice potrebbe interrompersi in un componente padre a causa di alcuni dati non validi e l’intera applicazione smette di funzionare, mentre invece nel primo caso l’app è resiliente e visualizza semplicemente alcuni campi vuoti in modo controllato.

Pensieri?

Segue un esempio semplificato:

Reagire componente

import React from 'react'; import PropTypes from 'prop-types'; import styles from './styles.css'; export const App = ({ person : { name, surname, address, subscription } = {} }) => ( 

{person.name}

{person.surname}

{person.address}

{ person.subscription && }
); // PS. this is incorrect in this example (as pointed out in an answer). Real code used inline initialization. // App.defaultProps = { // person: { subscription: undefined }, // }; App.propTypes = { person: PropTypes.shape({ name: PropTypes.string.isRequired, surname: PropTypes.string.isRequired, address: PropTypes.string, subscription: PropTypes.object, }).isRequired, };

Test

 import React from 'react'; import { shallow } from 'enzyme'; import { mockOut } from 'testUtils/mockOut'; import { App } from '../index.js'; describe('', () => { mockout(App, 'Subscription'); it('renders correctly', () => { const testData = { name: 'a name', surname: 'a surname', address: '1232 Boulevard Street, NY', subscription: { some: 'data' }, } const tree = shallow(); expect(tree.html()).toMatchSnapshot(); }); it('is resilient in case of bad data - still generates PropTypes validation logs', () => { const tree = shallow(); expect(tree.html()).toMatchSnapshot(); }); }); 

AGGIORNARE:

L’objective principale della domanda è se è corretto o meno assegnare valori predefiniti a oggetti contrassegnati con isRequired (invece di lasciare che la loro assenza interrompa il componente)

Recentemente mi è stato detto che non dovremmo farlo, che gli oggetti di scena sono ciò che ci aspettiamo dal genitore e se il contratto non viene rispettato per lasciare che il componente si rompa.

Esattamente, se un object nel componente è facoltativo, il componente (che esegue il rendering della vista effettiva) dovrebbe gestirlo, non il componente principale.

Tuttavia, è ansible che si verifichi una situazione in cui il genitore dovrebbe interrompere se uno qualsiasi dei componenti del figlio è in fase di interruzione. Posso pensare a due modi possibili per gestire questa situazione-

  1. Passando il notificatore di errore ai componenti figlio, dove se qualcosa va storto, il bambino può segnalare un errore al componente principale. Ma questa non è una soluzione pulita perché se ci sono N figli e se più di uno interromperà (o segnalerà un errore) a un genitore, non avremo nessun indizio e sarà difficile da gestire. [Questo non è affatto efficace ma scritto qui perché Ho seguito questo quando stavo imparando React: P]

  2. Usando try/catch nel componente padre e non fidarsi ciecamente di qualsiasi componente figlio e mostrare messaggi di errore quando qualcosa va storto. Quando si utilizza try/catch in tutti i componenti, è ansible generare in modo sicuro l’errore dai componenti quando un contratto non è soddisfatto.

Quale approccio è corretto e quali sono i pro e i contro?

IMO, il secondo approccio ( try/catch in components e throwing error quando i requisiti non sono soddisfatti) è valido e risolverà tutti i problemi. Durante la scrittura dei test per il componente quando gli oggetti di scena non vengono passati, ci si può aspettare un errore durante il caricamento del componente.

Aggiornare

Se stai usando React> 16, ecco come gestire gli errori.

Non è corretto assegnare valori predefiniti a .isRequred oggetti di scena .isRequred tramite componente defaultProps . Secondo i documenti ufficiali :

I valori predefiniti verranno utilizzati per garantire che this.props.name abbia un valore se non è stato specificato dal componente principale. Il typechecking di propTypes avviene dopo che i DefaultProp sono stati risolti, quindi typechecking si applicherà anche a defaultProps.

Se imposti il ​​valore della proprietà predefinito in Component.defaultProps, non riceverai mai un avviso se questa proprietà non viene fornita dal componente principale.

A mio parere, non lascerò uno o due attributi mancanti per rompere la mia domanda. React funge da livello di presentazione nella mia app e penso che sia molto lontano da mostrare “Oops! C’è qualcosa di sbagliato” quando non riesco a trovare una chiave in un object. Sembra un messaggio da un server gravemente danneggiato con stato 500, ma sappiamo che non è assolutamente sbagliato.

Per me, costruisco alcune regole per gestire le comunicazioni tra la funzione di rendering e defaultProps:

Diciamo che abbiamo un object utente passato dal genitore:

 defaultProps: { user: { avatar: { small: '' } } } 

nella funzione di rendering

 render() { const { user } = this.props; // if user.avatar is not defined or user.avatar.small is empty string or undefined then we render another component we have prepared for this situation. if (!user.avatar || !user.avatar.small) { return ( // components ... ); } // normal situation return ( // components ... ); } 

L’esempio sopra è per la stringa e abbiamo bisogno di un diverso strumento per altri tipi di dati.

In bocca al lupo.