2016-06-23 42 views
9

È possibile forzare un ListView a eseguire nuovamente il rendering, anche se i dati nel dataSource non sono stati modificati? Ho una ListView all'interno di una barra delle schede nella mia app e voglio che venga ridisegnata ogni volta che viene selezionata la scheda, indipendentemente dal fatto che i dati siano uguali o siano cambiati.React Native - Force ListView Eseguire nuovamente il rendering quando i dati non sono stati modificati

this.state = { 
    data: props.data, 
    dataSource: new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2}) 
} 

componentWillMount() { 
    this.setState({ 
    dataSource: this.state.dataSource.cloneWithRows(nextProps.data) 
    }) 
} 

render() { 
    <ListView 
    dataSource={this.state.data} 
    renderRow={this._renderRow} 
    /> 
} 

Ho provato a giocare con i rowHasChanged argomenti, ma che non ha aiutato. Qualsiasi aiuto sarebbe molto apprezzato

+0

magari aggiungere su una funzione di 'onClick' alla scheda e quando quella funzione viene chiamata lo rendono chiamare la funzione di rendering? – cameck

risposta

31

Così il vostro caso d'uso, se ho capito bene, è quello di ri-renderizzare tutte le righe della ListView causa di value cambiamenti. Non so cosa sia, fino a voi per determinarlo, ma userò value per spiegare la mia risposta:

Ci sono alcuni modi per risolvere questo problema. ogni hanno pro e contro:

  • modo semplice: basta<ListView key={value} ... /> !! Indica a React che ListView ha bisogno di ottenere il re-rendering quando il valore cambia (sarà smontato, rimontato).
  • "via hacky". sostituire con un nuovo DataSource. this.setState({ dataSource: new ListView.DataSource({ rowHasChanged: (r1, r2) => r1 !== r2 }).cloneWithData(this.props.data) });
  • il metodo (imo) migliore pratica: nei dati, incorporare value e implementare rowHasChanged correttamente. Ad esempio: (r1, r2) => r1.value !== r2.value && r1.data !== r2.data (< - questo è SOLO un modo possibile per rappresentare i dati, è necessario scegliere il formato, ho appena presunto che hai fatto clone dataSource con nextProps.data.map(data => ({ data, value })).

a me, la terza e ultima soluzione è la migliore perché:

  • è scalabile ben a più condizioni di ri-rappresentazione (altri value s)
  • può accadere che solo una parte delle tue righe dovrebbe essere nuovamente sottoposta a rendering e, in tal caso, non dovresti eseguire nuovamente il rendering di tutte le righe per ottenere prestazioni migliori.

qui è un altro risposta che ho dato su questo argomento:

What are the exact inputs to rowHasChanged in ListView.DataSource

+0

Grazie per la risposta, la mia mente è saltata. Non avevo idea di r1 e r2 significava vecchia e nuova riga. Ha molto più senso ora. Magari avessero potuto fare riferimento a questo meglio! –

+1

Ci sono alcuni casi in cui la "via hacky" sembra essere l'unico modo per fare ciò di cui hai bisogno. Ad esempio, sto lavorando a un caso in cui un modello nell'elenco dei modelli visualizzati da ListView è stato modificato da qualche altra parte nell'app. Per mostrare questo cambiamento, dobbiamo modificare l'intero DataSource, perché ListView non farà nulla se non lo farai. È opinione di uno sviluppatore di React che un'intera lista debba essere considerata immutabile come gli oggetti all'interno di quella lista. – mienaikoe

+1

@mienaikoe che implementa 'rowHasChanged' è in effetti l'opportunità di scegliere se consideri il tuo oggetto lista immutabile o meno. Dipende solo da te implementarlo e solo il predefinito suggerito 'r1! == r2' è un" parere dello sviluppatore di React "in favore di oggetti di immutabilità ma il ListView stesso non è quell'opinione (invece di WindowedListView sperimentale per esempio) . 'rowHasChanged' ti dà il controllo della funzione di uguaglianza e immutabilità o meno dei tuoi dati. BTW tutta questa cosa è un compromesso di prestazioni e forse troveremo qualcosa di meglio in futuro. – gre

0

È possibile chiamare this.forceUpdate() per forzare un nuovo rendering.

È possibile eseguire questa operazione in un semplice gestore onClick da allegare alla scheda.

Maggiori informazioni here, on the official docs

Tuttavia, se ci sono altre regole che dettano se ci deve essere un aggiornamento o no, si può provare a sostituire la dataSource tutto con uno nuovo, che lo rende hanno un nuovo riferimento. Questo lo renderà anche aggiornato.

Qualcosa sulla falsariga di

this.setState({ data : newDataSource… });

2

utilizzare un metodo Immutabilità

Il modo migliore per "aggiornare" questo sarebbe di clonare l'elemento di dati in domanda piuttosto che fare una modifica della bandiera come lo è lo r1 !== r2. Invece di aggiornare un singolo oggetto, cambia il "puntatore" stesso clonando.

Per questo problema particolare aggiornare l'elemento nell'array in questo modo.

arr[i] = { ...arr[i], someKey: newValue } 

Questo sostituirà la proprietà "someKey" con "newValue" e la riga corrente avrà un nuovo puntatore.

Qui è una buona risorsa per conoscere l'immutabilità in JavaScript moderna che reagiscono usi autoctoni: https://wecodetheweb.com/2016/02/12/immutable-javascript-using-es6-and-beyond/

0

Questo è probabilmente un po 'eccessivo, ma io sono solo di sostituire l'origine dati su componentDidUpdate (forse questo è un male per le prestazioni? Non so haha)

componentWillMount() { 
    this.ds = new ListView.DataSource({ 
    rowHasChanged: (r1, r2) => r1 !== r2, 
    }); 
    this.dataSource = this.ds.cloneWithRows(this.props.shiftsList); 
    } 

componentDidUpdate() { 
    this.ds = new ListView.DataSource({ 
    rowHasChanged: (r1, r2) => r1 !== r2, 
    }); 
this.dataSource = this.ds.cloneWithRows(this.props.shiftsList); 
} 
0

So che sono un po 'in ritardo, ma sono sicuro che questo è ancora rilevante. C'è un approccio ancora più semplice a questo ... Il primo esempio fornito da Jason è sicuramente una buona soluzione, ma sto pensando a quelli che facilmente si confondono. Se è così, usa questo.

// On component initalization 
componentWillMount() { 
    this.UpdateData(); 
} 

// When the component updates 
componentDidUpdate() { 
    if (this.state.data != this.props.data) 
     this.UpdateData(); 
} 

// Function to update the ListView component data 
UpdateData() { 
    var source = new ListView.DataSource({ 
     rowHasChanged: (r1, r2) => r1 !== r2 
    }); 

    this.setState({ 
     data: this.props.data, 
     dataSource: source.cloneWithRows(this.props.data) 
    }); 
} 

Si consiglia inoltre di implementare Redux nella propria applicazione. In questo modo è possibile memorizzare il nuovo valore di array aggiornato in un riduttore e quindi memorizzarlo localmente in uno stato e successivamente utilizzare un'istruzione if nella funzione componentDidUpdate per determinare se i dati vengono aggiornati o meno.

Il Reagire tronchi debugger lamentele prestazioni basato o avvertimenti di sorta ...