Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to update React Native ListView when Realm data changes

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>

)
...
}
like image 799
amirfl Avatar asked May 30 '16 15:05

amirfl


2 Answers

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=... />
  }
}
like image 114
tiby Avatar answered Sep 18 '22 12:09

tiby


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
    };
  },
})
like image 26
jasonmerino Avatar answered Sep 20 '22 12:09

jasonmerino