问题
I have a list of 'Activity' objects stored in a Realm DB which are showing on a ListView. There is no problem initially loading the data and showing it on the screen. However, when the data update (in another screen, lets call it 'detail edit screen') and the user returns to the list screen, the list isn't updated.
I tried to setState inside the render method, but that returns a blank screen.
var activities = realm.objects('Activity').sorted('date');
let ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
...
module.exports = React.createClass({
getInitialState: function(){
return {
dataSource: ds.cloneWithRows(activities),
};
},
...
render: function(){
//update the list of activities
activities = realm.objects('Activity').sorted('date');
this.setState({dataSource: ds.cloneWithRows(activities)});
return (
<View>
{(!activities || activities.length<1) && noActivitiesMessage}
<ListView style={styles.listStyle} dataSource={this.state.dataSource}
renderRow={this._renderRow} />
</View>
)
...
}
回答1:
I had the same problem like you. My problem was solve by using the Realm ListView. I use it like this
import { ListView } from 'realm/react-native';
class List extends Component {
constructor(props) {
super(props);
}
componentDidMount() {
src = realm.objects('MyObject');
this.setState({
dataSource:this.state.dataSource.cloneWithRows(src),
data:src
});
}
updateData(){
// update or add object in Realm
this.setState({
dataSource:this.state.dataSource.cloneWithRows(this.state.data)
});
}
render(){
return(
<ListView
dataSource={this.state.dataSource}
renderRow=... />
}
}
回答2:
I would suggest using the react-native-realm package on NPM (full disclosure, I'm the author, so I'm a little biased). It uses the provider pattern (similar to react-redux) to provide realm data and automatic change event listening (as well as automatic removal of those event listeners) to your components by simply wrapping them in the connectRealm higher order component and defining which schemas and where to map them in the props of your component.
You could use it like this. In the top level component of your app wrap the sub components with RealmProvider.
// App.js
// the file you use to wire up your realm schemas, etc.
import realm from './path/to/your/realm/file';
import { RealmProvider } from 'react-native-realm';
import List from './List';
class App extends React.Component {
render() {
<RealmProvider realm={realm}>
<List />
</RealmProvider>
}
export default App;
And then in your sub components if they need realm data use the connectRealm function to hook into that data.
// List.js
import { ListView } from 'realm/react-native';
import { connectRealm } from 'react-native-realm';
class List extends Component {
constructor(props) {
super(props);
this.state = {
dataSource: new ListView.DataSource({
rowHasChanged: (r1, r2) => r1 !== r2,
}).cloneWithRows(props.activities);
};
}
componentWillReceiveProps(nextProps) {
this.setState({
dataSource: this.state.dataSource.cloneWithRows(nextProps.activities),
});
}
render(){
return (
<ListView
dataSource={this.state.dataSource}
renderRow=...
/>
)
}
}
export default connectRealm(List, {
schemas: ['Activity'],
mapToProps({ activities }) {
return {
activities: activities.sorted('date'), // do extra filtering and sorting here
};
},
})
回答3:
As you already mentioned in your comment, setState may not be called in the render function (it would basically cause an infinite recursion).
The easiest solution here is to add a listener in the constructor, which would look like this for your example:
realm.addListener('change', () => {
this.setState({dataSource: realm.objects('Activity').sorted('date'); })
});
回答4:
Here's my approach, thanks to @Johannes's answer and @tiby's suggestion to use realm's listview.
constructor (props) {
super(props)
this.state = {
ds: new ListView.DataSource({
rowHasChanged (a, b) {
return a.done !== b.done || a.text !== b.text || a.items || b.items
}
}),
src: realm.objects(‘Object’),
}
realm.addListener('change', () => {
this.setState({src: realm.objects(‘Object’) })
});
}
render () {
var dataSource = this.state.ds.cloneWithRows(this.state.src)
return (
<ListView
dataSource={dataSource}
…
/>
)
}
来源:https://stackoverflow.com/questions/37529058/how-to-update-react-native-listview-when-realm-data-changes