Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Realm & React Native - Best practice to implement auto-updates?

What are the best practices/patterns make realm a reactive datasource in a react native app? Especially for presentational and container components pattern?

Here is an example which I'd like to make reactive: Realm with React Native

The docs on auto-updates/change-events are a bit thin and the official example does not make use of this feature (to my knowledge).

like image 824
ThorbenA Avatar asked Mar 09 '16 20:03

ThorbenA


3 Answers

I think @Ari gave me a good answer for redux folks as i was also struggling. I'm not sure if it's immutable enough but it works!

I'm simpliy dispatching getVehicles action inside addListener and it just works!

Below is UI component whose constructor function makes the magic!

//- importing my realm schema
import realm from '../../db/models';
//- Importing my action
import { getVehicles } from './../../actions/vehicle';


@connect((store) => {
    return {
        vehicle: store.vehicle.vehicles
    }
})
export default class Devices extends Component {
    constructor(props) {
        super(props);

        realm.addListener('change', () => {
            props.dispatch(getVehicles());
        });

    }
} 

Below is db/models file used up there in the constructor.

import Realm from 'realm';

class VehicleSchema {};

VehicleSchema = {
    name: 'vehicleInfo',
    properties: {
        vehicleName: 'string',
        vehicleNumber: 'string',
        vehiclePassword: 'string',
        vehiclePasswordTrigger: 'bool',
        vehicleType: 'string',
        vehiclePicture: { type: 'data', optional: true }
    }
};

export default new Realm({schema: [VehicleSchema]});

Below is the actions/vehicle file, which gets dispatched in the constructor above.

import { queryVehicle } from './../db/queryVehicle';

export function getVehicles() {

    const vehicles = queryVehicle();

    return function(dispatch) {
        dispatch({type: "GOT_VEHICLES", payload: vehicles});
    }
}

Below is my queryVehicle function that does the querying called in action file above.

import vehicleModel from './models';

const queryVehicle = (queryInfo="vehicleInfo", filter='') => {

    const objects = vehicleModel.objects(queryInfo);

    if(filter.length === 0) return objects;

    let results = objects.filtered(filter);
    return results;


};

export { queryVehicle };

disclaimer I don't know if this code looks immutable enough, or following good redux practice cause i'm just starting out with redux so give me some comments advising if i'm doing something wrong.

I'll also guess reducer implementation wouldn't matter much in this here.

like image 21
ArchNoob Avatar answered Oct 24 '22 09:10

ArchNoob


You can make your example reactive by subscribing to events and updating the ui when you receive a change event. Right now events are only sent when write transactions are committed, but finer grained change events will be added in the future. For now you could add the following constructor to update the ui on changes:

constructor(props) {
  super(props);
  this.realm = new Realm({schema:[dogSchema]})
  this.realm.addListener('change', () => {
    this.forceUpdate()
  });
}

You need to hold onto a Realm instance to keep the notifications alive, and you can use this Realm instance throughout the rest of the component.

Instead of calling forceUpdate, you could instead set the component's state or props within the event listener to trigger the refresh, like so:

constructor(props) {
  super(props);

  this.realm = new Realm({schema:[dogSchema]})

  this.state = {...}; // Initial state of component.

  this.realm.addListener('change', () => {
    this.setState({...}); // Update state instead of using this.forceUpdate()
  });
}
like image 64
Ari Avatar answered Oct 24 '22 08:10

Ari


Recently ran into an issue with Realm ListView auto-updating. When the ListView rows have varied heights, you can get overlaps on rows in the UI. The below was the only way I could get the ListView to re-render without causing UI overlaps. It seems a bit "dirty" to me, so if there is a better way, I welcome the input. But this is working perfectly so far; incase anyone else runs into this issue.

Basically it just wipes the dataSource, then inserts it again using the setState callback when there are insertions or deletions, but modifications simply roll through and auto-update.

let feed = this.props.store.feed;

feed.addListener((name, changes) => {
   if (changes.insertions.length || changes.deletions.length) {
       this.setState({dataSource: this.ds.cloneWithRows([])},
           () => this.setState({dataSource: this.ds.cloneWithRows(feed)})
       );
   } else {
        this.setState({dataSource: this.ds.cloneWithRows(feed)});
   }
});
like image 1
fostertime Avatar answered Oct 24 '22 09:10

fostertime