ReactJS – Sollevamento dello stato rispetto al mantenimento di uno stato locale

Nella mia azienda stiamo migrando il front-end di un’applicazione web a ReactJS. Stiamo lavorando con create-react-app (aggiornato a v16), senza Redux. Ora sono bloccato su una pagina la cui struttura può essere semplificata dalla seguente immagine:

Struttura della pagina

I dati visualizzati dai tre componenti (SearchableList, SelectableList e Map) vengono recuperati con la stessa richiesta di backend nel metodo componentDidMount() di MainContainer. Il risultato di questa richiesta viene quindi memorizzato nello stato di MainContainer e presenta una struttura più o meno simile a questa:

 state.allData = { left: { data: [ ... ] }, right: { data: [ ... ], pins: [ ... ] } } 

LeftContainer riceve come object state.allData.left da MainContainer e passa props.left.data a SearchableList, ancora una volta come prop.

RightContainer riceve come stato state.allData.right da MainContainer e passa props.right.data a SelectableList e props.right.pins a Map.

SelectableList visualizza una casella di controllo per consentire azioni sui suoi elementi. Ogni volta che si verifica un’azione su un elemento del componente SelectableList, potrebbe avere effetti collaterali sui pin Mappa.

Ho deciso di memorizzare nello stato di RightContainer un elenco che mantiene tutti gli ID degli elementi visualizzati da SelectableList; questa lista è passata come oggetti di scena sia a SelectableList che a Map. Poi passo a SelectableList un callback, che ogni volta che viene effettuata una selezione aggiorna la lista di id all’interno di RightContainer; nuovi oggetti di scena arrivano sia in SelectableList che in Map, e così render() viene chiamato in entrambi i componenti.

Funziona bene e aiuta a mantenere tutto ciò che può accadere a SelectableList e Map all’interno di RightContainer, ma sto chiedendo se questo è corretto per i concetti di sollevamento-stato e single-source-of-truth .

Come alternativa fattibile ho pensato di aggiungere una proprietà _selected a ogni object in state.right.data in MainContainer e passare i tre livelli di callback selezionati a SelectableList, gestendo tutte le possibili azioni in MainContainer. Ma non appena si verifica un evento di selezione questo finirà per forzare il caricamento di LeftContainer e RightContainer, introducendo la necessità di implementare logiche come shouldComponentUpdate() per evitare il render() inutile render() particolare in LeftContainer.

Qual è / potrebbe essere la soluzione migliore per ottimizzare questa pagina dal punto di vista architettonico e prestazionale?

Di seguito hai un estratto dei miei componenti per aiutarti a capire la situazione.

MainContainer.js

 class MainContainer extends React.Component { constructor(props) { super(props); this.state = { allData: {} }; } componentDidMount() { fetch( ... ) .then((res) => { this.setState({ allData: res }); }); } render() { return ( 
); } } export default MainContainer;

RightContainer.js

 class RightContainer extends React.Component { constructor(props) { super(props); this.state = { selectedItems: [ ... ] }; } onDataSelection(e) { const itemId = e.target.id; // ... handle itemId and selectedItems ... } render() { return ( 
this.onDataSelection(e)} selectedItems={this.state.selectedItems} />
); } } export default RightContainer;

Grazie in anticipo!

Come dichiarano i documenti di React

Spesso, diversi componenti devono riflettere gli stessi dati mutevoli. Raccomandiamo di sollevare lo stato condiviso fino al loro antenato più vicino.

Ci dovrebbe essere una singola “fonte di verità” per tutti i dati che cambiano in un’applicazione React. Di solito, lo stato viene prima aggiunto al componente che ne ha bisogno per il rendering. Quindi, se anche altri componenti ne hanno bisogno, puoi sollevarlo fino al loro antenato più vicino. Invece di provare a sincronizzare lo stato tra diversi componenti, è necessario fare affidamento sul stream di dati top-down.

Lo stato di sollevamento implica la scrittura di un codice più “standard” rispetto agli approcci vincolanti a doppio senso, ma come vantaggio, ci vuole meno lavoro per trovare e isolare i bug. Poiché qualsiasi stato “vive” in alcuni componenti e solo quel componente può cambiarlo, l’area di superficie per i bug viene notevolmente ridotta. Inoltre, è ansible implementare qualsiasi logica personalizzata per rifiutare o trasformare l’input dell’utente.

Quindi in sostanza è necessario sollevare tali stati nell’albero che viene utilizzato anche nel componente Siblings. Quindi la prima implementazione in cui si memorizzano gli elementi selectedItems come stato nel RightContainer è completamente giustificata e un buon approccio, poiché il genitore non ha bisogno di essere informato e questi data vengono condivisi dai due componenti child di RightContainer e questi due ora avere un’unica fonte di verità.

Secondo la tua domanda:

Come alternativa fattibile ho pensato di aggiungere una proprietà _selected a ogni object in state.right.data in MainContainer e passare i tre livelli di callback selezionati a SelectableList , gestendo tutte le possibili azioni in MainContainer

Non sarei d’accordo sul fatto che questo è un approccio migliore rispetto al primo, dal momento che MainContainer non ha bisogno di conoscere gli elementi selectedItems o il gestore degli aggiornamenti. MainContainer non sta facendo nulla per questi stati e lo sta semplicemente MainContainer .

Considera di optimise on performance , tu stesso parli dell’implementazione di shouldComponentUpdate , ma puoi evitarlo creando i tuoi componenti estendendo React.PureComponent che implementa essenzialmente shouldComponentUpdate con un confronto shallow di state e props .

Secondo i documenti:

Se la funzione render() del componente React restituisce lo stesso risultato dato gli stessi oggetti di scena e lo stesso stato, in alcuni casi è ansible utilizzare React.PureComponen t per un aumento delle prestazioni.

Tuttavia, se più componenti profondamente annidati fanno uso degli stessi dati, ha senso utilizzare redux e archiviare tali dati nello stato redux. In questo modo è globalmente accessibile a tutta l’App e può essere condivisa tra componenti non direttamente correlati.

Ad esempio, considera il seguente caso

 const App = () => {     } 

Ora qui se sia Home che MyComp vogliono accedere agli stessi dati. Potresti passare i dati come oggetti di scena dall’app chiamandoli tramite il puntello di render . Tuttavia, si farebbe facilmente collegando entrambi questi componenti allo stato di Redux usando una funzione di connect come

 const mapStateToProps = (state) => { return { data: state.data } } export connect(mapStateToProps)(Home); 

e allo stesso modo per MyComp . Inoltre è facile configurare le azioni per aggiornare le informazioni rilevanti

Inoltre è particolarmente facile configurare Redux per la tua applicazione e potresti memorizzare i dati relativi alle stesse cose nei singoli riduttori. In questo modo sarai in grado di modulare anche i dati dell’applicazione

I miei onesti consigli su questo. Dall’esperienza è:

Redux è semplice. È facile da capire e scalare, ma si dovrebbe usare Redux per alcuni casi d’uso specifici.

Dato che Redux incapsula la tua app puoi pensare di immagazzinare cose come:

  • impostazioni locali dell’app corrente
  • attuale utente autenticato
  • gettone corrente da qualche parte

Cose di cui avresti bisogno su scala globale. react-redux consente anche un decoratore @connect sui componenti. Così come:

 @connect(state => ({ locale: state.locale, currentUser: state.currentUser })) class App extends React.Component 

Questi sono tutti tramandati come oggetti di scena e connessione possono essere utilizzati ovunque sull’App. Anche se mi raccomando di tramandare gli oggetti di scena globali con l’operatore di diffusione

  

Tutti gli altri componenti (o “pagine”) all’interno della tua app possono fare il loro stato incapsulato. Ad esempio, la pagina Utenti può fare la propria cosa.

 class Users extends React.Component { constructor(props) { super(props); this.state = { loadingUsers: false, users: [], }; } ...... 

Dovresti accedere alle impostazioni locali e currentUser tramite gli oggetti di scena perché sono stati passati dai componenti Container.

Questo approccio l’ho fatto più volte e funziona.

Ma , dal momento che volevi davvero consolidare la conoscenza di React, prima di eseguire Redux puoi semplicemente memorizzare il tuo stato sul componente di livello superiore e passarlo ai bambini.

Svantaggi:

  • Dovrai continuare a passarli in componenti di livello interno
  • Per aggiornare lo stato dai componenti di livello interno devi passare la funzione che aggiorna lo stato.

Questi aspetti negativi sono un po ‘noiosi e scomodi da gestire. Ecco perché Redux è stato costruito.

Spero di aver aiutato. in bocca al lupo

Usando Redux puoi evitare tali callback e mantenere l’intero stato in un singolo negozio – quindi crea il tuo componente componente connesso genitore – e crea componenti di destra e di sinistra stupidi – e passa i puntelli che ottieni da genitore a figlio – e tu non devi preoccuparti dei callback in questo caso.