How to update React Native ListView when Realm data changes

白昼怎懂夜的黑 提交于 2019-12-06 03:10:37

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!