Autore: Stefano Frasca
Gli indicatori di avanzamento informano gli utenti sullo stato dei processi in corso, come il caricamento di un’app, lo stato di una richiesta remota, l’invio di un modulo o il salvataggio degli aggiornamenti. Comunicano, inoltre, visivamente lo stato di un’app e indicano le azioni disponibili.
Sviluppando un’app in React Native molto spesso è necessario avere la possibilità di inserire un loading indicator che occupi, in overlay, tutta la dimensione dello Screen così da impedire azioni nella UI, durante l’avanzare di un processo.
Per la nostra prossima app in React Native, stiamo usando, per alcune viste, questo approccio inserendo un loading ‘bloccante‘ quando, al componentDidMount, viene eseguita una request remota alle nostre API per ottenere delle informazioni (assenze, voti, etc…).
Questo loading è mostrato tramite un Modal con background ‘transparent‘ che va in overlay su tutto lo Screen corrente: così facendo all’utente viene impedita l’esecuzione di qualsiasi azione sulla UI, garantendoci l’impossibilità di side-effects e nel contempo un feedback visuale. Al termine della chiamata remota il loading viene rimosso tramite la props ‘visible‘ della Modal.
Andiamo a vedere, tramite qualche snippet di codice, come abbiamo implementato ciò.
Partiamo dal componente Indicator che rappresenta la Modal con l’ActivityIndicator interno
Come utilizzare il componente Indicator internamente al nostro Screen? Nel dettaglio:
- Nello stato interno del componente Screen avremmo la proprietà loading settata a false
- Al componentDidMount o componentWillMount (attenzione, questo verrà presto deprecato) laddove viene eseguita la richiesta asincrona
- setState -> loading: true
- await API call
- setState -> loading: false
- Nel metodo render() del componente Screen aggiungere il componente Indicator con la property isLoading impostata su {this.state.loading}: i due setState sopra mostrano/nascondono la modale che contiene il loading.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
import ... class MyScreen extends Component { _isMounted = false; static contextType = StateContext; static navigationOptions = ({ navigation }) => { return { headerTitle: ( <HeaderApp navigation={navigation} title={capitalize(NavigationConf.TITLE)} info={<Legenda type={NavigationConf.TITLE} />} /> ) }; }; constructor(props) { super(props); this.state = { loading: false, }; } //... _getData = async (id) => { this.setState({ loading: true, }); const data = await api.getRemoteData(id); this.setState({ data, loading: false }); }; async componentDidMount() { const [{ dataId }] = this.context; await this._getData(dataId); } //... render() { return ( <DismissKeyboardOnBlur> <View style={style.general}> <View style={[style.container]} keyboardDismissMode={'interactive'} keyboardShouldPersistTaps={'handled'} scrollEnabled={true} > <Indicator isLoading={this.state.loading} /> <View style={styleResponsive.style.containerCalendar} key={this.state.idRendering}><Text>SOME CONTENT</Text></View> </View> </View> </DismissKeyboardOnBlur> ); } } export default MyScreen; |
L’esempio sopra è solo un barebone, funzionante, ma naturalmente da sviluppare e ampliare sia funzionalmente che graficamente.