I ran into a problem when getting started with realm where I didn't know how to update a react component when the data changed for a realm query.
Finally found this answer on stack overflow which was super helpful: http://stackoverflow.com/questions/35902361/realm-react-native-best-practice-to-implement-auto-updates
The docs have no specific answers for this, and the example app actually solves this issue slightly differently by passing props. I imagine this is probably one of the first things anyone using realm would run into so giving some clear guidelines in docs would be a great idea IMO.
Happy to set up a PR for this.
You don't need to subscribe to realm and call this.forceUpdate() as long as you are using Realm.Results or Realm.Object as they are reactive by default.
If you are dealing with non-reactive values that come from Realm, then you can subscribe to realm but instead of calling this.forceUpdate(), you should re-fetch the data and simply do this.setState and update only the value you need.
I recommend creating a data wrapper for your data sources to fetch these Realm.Results or Realm.Object since your components should not need to know that the data is coming from Realm.
@bosung90 Thanks for the comment. I tried making this work and saving new data will not update the view reactively. I need to refresh to see changes.
Heres my test code. Basically, when create() runs, the realm.objects('Dog').length does not change, which tells me that react is not seeing a change in data, and not re-rendering the component. A manual refresh does show the new data.
export default class testrealm extends Component {
render() {
let realm = new Realm({
schema: [{name: 'Dog', properties: {name: 'string'}}]
});
let create = () => {
realm.write( () => realm.create('Dog', {name: 'Rex'}) );
}
return (
<TouchableHighlight style={{flex:1,alignItems: 'center', padding: 20}} onPress={create}>
<View>
<Text>Count is: {realm.objects('Dog').length}</Text>
</View>
</TouchableHighlight>
);
}
}
Maybe I'm doing something wrong in my example?
Versions:
"react": "15.3.2",
"react-native": "0.36.0",
"realm": "^0.14.3"
if it is for simple bits I use this component:
class Reactive extends Component {
_realm = new Realm(); // Note: this gets the default realm so you don't have to describe your schema again
componentWillMount () {
this._realm.addListener('change', this._forceUpdate);
}
componentWillUnmount () {
this._realm.removeListener('change', this._forceUpdate);
}
render () {
const objects = this.props.name && this._realm.objects(this.props.name);
return this.props.render(objects);
}
_forceUpdate = () => this.forceUpdate();
}
Then use it like so:
<Text>Count: <Reactive name='Dog' render={(objects) => <Text>{objects.length}</Text>} /></Text>
or if I've got the realm in scope:
<Text>Count: <Reactive render={() => <Text>{realm.objects('Dog').length}</Text>} /></Text>
The problem is that realm doesn't allow scoping of the changes... so ANY change will force an update on the <Reactive /> component. To fight that you could throttle the forceUpdate like so:
class Reactive extends Component {
_realm = new Realm(); // Note: this gets the default realm so you don't have to describe your schema again
componentWillMount () {
- this._realm.addListener('change', this._forceUpdate);
+ this._realm.addListener('change', this._throttledForceUpdate);
}
componentWillUnmount () {
- this._realm.removeListener('change', this._forceUpdate);
+ this._realm.removeListener('change', this._throttledForceUpdate);
}
render () {
const objects = this.props.name && this._realm.objects(this.props.name);
return this.props.render(objects);
}
_forceUpdate = () => this.forceUpdate();
+ _timer = null
+ _throttledForceUpdate = () => {
+ clearTimeout(this._timer)
+ this._timer = setTimeout(this._forceUpdate, 100)
+ }
}
Hey guys,
as @ksokhan I was faced with the issue of updating the view when changes to the realm happens. The pointed stackoverflow thread is basically so far the only answer and people seem to be doing that. I tried the proposed solution and it works, but I think that we need something more in a complex app. Most of us are dealing with apps that don't have a single screen for everything.
In my case we use the react navigation and have several scenes that show different stuff to the user. I have nested objects like this
companies -> fields -> comments
a company scheme has a list property to fields and a field has a list property to comments. Now that we have collection notifications I'm subscribing to them in componentWillMount().
in the scene for companies I'm doing
realm.objects('Company').addListerer...
in fields i do
realm.objects('Fields).addListenere.. etc
now this really works. the views are getting updated, but what I noticed is that whenever I add a comment a change event is emitted on the Company collections as it most probably links to fields and then to comments. The change event is emitted to fields as well. So I end up in a situation where I'm re-rendering non-visible screens. And the deeper I go in the navigation, the more scenes get re-rendered in the backend when something in realm changes.
That's not necessarily bad if your scenes in the stack depend on the new data, but that is not always the case and I can't seem to figure out how to make a decision - a data that this view depends on has changed -> update it.
The changes object that is passed to the listener always says - 1 modification in company, 1 modification in fields, 1 insertion in comments. How am I supposed to figure out in Companies if the changes to the company object should re-render the view or not?
Hey folks,
Seems like there are a couple things going on in this issue: basic re-rendering of data when it changes in realm and more optimized re-renders based on listening to specific changes.
I have been working on an app this year that uses realm and I wanted to abstract away the manual adding and removing of event listeners in each of my components that needs data from realm. In doing this I wrote a small wrapper for doing this in a much easier manner called react-native-realm.
This library handles pushing data into components from realm whenever it changes using the provider pattern which you can see at work in redux and other libraries.
I mention this here for two reasons, one to let you guys know about the library as a rudimentary solution, and two, to hopefully collect feedback about how to make the library better so that you can listen to specific changes that happen and react to those (which the library doesn't support right now, but has been a request).
Anyway, hope my solution can be of help to anyone and if anyone has any feedback or ideas to make it better I would love to hear them.
Most helpful comment
Hey folks,
Seems like there are a couple things going on in this issue: basic re-rendering of data when it changes in realm and more optimized re-renders based on listening to specific changes.
I have been working on an app this year that uses realm and I wanted to abstract away the manual adding and removing of event listeners in each of my components that needs data from realm. In doing this I wrote a small wrapper for doing this in a much easier manner called react-native-realm.
This library handles pushing data into components from realm whenever it changes using the provider pattern which you can see at work in redux and other libraries.
I mention this here for two reasons, one to let you guys know about the library as a rudimentary solution, and two, to hopefully collect feedback about how to make the library better so that you can listen to specific changes that happen and react to those (which the library doesn't support right now, but has been a request).
Anyway, hope my solution can be of help to anyone and if anyone has any feedback or ideas to make it better I would love to hear them.