I'm trying to update the UI of my app when there is an update of realm data from the server/client.
I have a listener on a Realm Object for getting updates. When there is an update on the server (or in the client) the function provided to the listener calls setState({}).
The UI updates when render function is called.
The strange part is that even if the console says that everything is ok, and it shows that the render method was called with correct data, I can't see any updates to my app.
If I tap on the screen randomly (after 1s,2s, 20s...) the UI magically updates and everything is correct.
If I do the same setState with a function called from a button it works, I guess because the animation of the button triggers the UI updates.
You have to update the server_url and credential in order to work.
react-native init test
npm install realm
react-native link realm
`App.js
import React, {Component} from 'react';
import {Platform, StyleSheet, Text, View} from 'react-native';
import Realm from 'realm'
import { SERVER_URL } from "./config/realm";
import { Utente } from "./config/schema";
export default class App extends Component {
loginAsync = async () => {
var realm_user = Realm.Sync.User.current
if(!realm_user){
const credentials = Realm.Sync.Credentials.usernamePassword('admin', '******' ,false);
realm_user = await Realm.Sync.User.login(SERVER_URL, credentials);
}
const config = realm_user.createConfiguration({
schema: [
Utente,
Realm.Permissions.Permission,
Realm.Permissions.User,
Realm.Permissions.Role],
schemaVersion: 1,
});
this.realm = new Realm(config);
var connectedUserData = this.realm.objects("Utente").filtered("id = $0", realm_user.identity)
connectedUserData.subscribe()
connectedUserData.addListener((connectedUserData)=>{
if(connectedUserData[0]){
this.setState({
connectedUserData: connectedUserData[0]
})
}
})
}
constructor(props){
super(props)
this.loginAsync()
this.state = {
connectedUserData: {
nome: 'not loaded'
}
}
}
render() {
return (
<View style={styles.container}>
<Text>{ this.state.connectedUserData.nome }</Text>
</View>
);
}
}
//Schema.js
export const Utente = {
name: "Utente",
primaryKey: "id",
properties: {
id: "string",
nome: 'string?',
permissions: '__Permission[]'
}
}
//Package.json
{
"name": "testBaseRealm",
"version": "0.0.1",
"private": true,
"scripts": {
"start": "node node_modules/react-native/local-cli/cli.js start",
"test": "jest"
},
"dependencies": {
"react": "16.6.3",
"react-native": "0.57.7",
"realm": "^2.27.0-rc.3"
},
"devDependencies": {
"@babel/core": "7.4.4",
"@babel/runtime": "7.4.4",
"babel-jest": "24.8.0",
"jest": "24.8.0",
"metro-react-native-babel-preset": "0.54.1",
"react-test-renderer": "16.6.3"
},
"jest": {
"preset": "react-native"
}
}
UPDATE
Tested also building on Mac, tested with a more simple schema, with a new realm, etc...
It seems we are having the same issue in our project.
We have a search field and every time we type it creates a new Realm Results with filtered data. We add a listener on it to set the state but it is not triggered. It only gets triggered if some other prop/state is changed.
Strangely enough when we add a console.warn the listener is always triggered.
@jurmous It may be something related with this:
After 10 days, I'm not able to solve this...It's a core feature and seems I haven't made mistakes...Is there anybody here?
@bmunkholm @kneth @ericjordanmossman
@aureliopetrone Maybe add a listener to the subscription and wait for the subscription to be ready before updating the UI. Relevant doc: https://realm.io/docs/javascript/latest/api/Realm.Sync.Subscription.html
@kneth the listener get called and the data received is correct. So with that data I simple do a setState(), it updates well but I can't see any changes on view until I tap somewhere.
Can you try to call setState after a slight delay?
@kneth I read the doc multiple times. I'm quite sure I'm doing everything correct. It's not my first app with Realm and this is a thing that worked well before. (
@nirinchev I tried this way, and it doesn't work (the code is slighty different because of the tests i'm doing)
setTimeout(()=>{
this.setState({
loved_articles : connectedUserData.loved_articles.map(el=>el.id)
})
}, 500)
The state updates, the render method is called, but view doesn't update
@kneth I sent a zipped version of the app, as he asked, to @ericjordanmossman via support ticket but it's been 5 days since last updates
UPDATE
It seems to work better, but the bug is still there
It's strange because I tried this way before...but with 0 as parameter not 500. Why I have to do this? How can I be sure this solve the problem anywhere?
I'm not sure, I have never written a React app. I just thought it could be due to the js thread being busy that the setState call doesn't update the UI, so I thought executing it with a delay would allow whatever is keeping the thread busy to complete its work before handing it off to React.
Should this be a issue?
Is the issue that setState() is asynchronous?
@kneth the issue is that when you call it nothing happens and no error is logged. I don't know if this is how it is supposed to works, but this thing needs to be more clear in the doc I believe
@aureliopetrone I haven't been able to reproduce it using an iOS simulator or an Android emulator. The delay after not loaded is so short that I almost cannot see not loaded.
@kneth you can see not_loaded changing, but if you change the value on Realm Studio, it doesn't updates. It's like it updates only on the first call.
I will provide you a video
@kneth I sent a video to support and the complete source code of the App @ericjordanmossman
Any news guys? @ericjordanmossman @kneth
We looked into the issue and it is that Realm always returns the same RealmResults instance on result. So many components do a shallow equal check === which did not change after a data update. Now when we receive the realm results we first put it into a new array [...results] which we store as state after which react picks up on the changes. Somehow after some ui interaction the UI was forced to rerender which caused this behaviour.
It seems in your case the connectedUserData[0] is the same instance as before.
@jurmous, I solved inseriting this line
setTimeOut(()=>null)
before running setState. If you are interested in this see this post on stackoverflow:
Is there any update on this?
No. It appears you have a workaround for now? Other issues without workarounds are getting higher priority.
Yes, but I cannot understand why this happens and I'm not sure this will solve the problem on every device. Despite using this workaround I've noticed that the problem still persists on some iOS devices.
So the problem is platform-independent (iOS, Android, Emulator), I guess it's related to how React NAtive callbacks work :(
It's very annoying :P
I created another app reproducing the same behaviour - I'll close this issue as a duplicate of the new one. Thanks for reporting this @aureliopetrone! https://github.com/realm/realm-js/issues/2655
Most helpful comment
@jurmous, I solved inseriting this line
setTimeOut(()=>null)before running setState. If you are interested in this see this post on stackoverflow:
https://stackoverflow.com/questions/56725797/how-to-prevent-react-native-from-stucking-on-es6-promises/56729456#56729456