realm-js: UI doesn't update until tap on the screen when setState is called inside a realm listener callback
Goals
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({}).
Expected Results
The UI updates when render function is called.
Actual Results
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.
Steps to Reproduce
You have to update the server_url and credential in order to work.
react-native init test npm install realm react-native link realm
Code Sample
`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"
}
}## Version of Realm and Tooling
- React / React Native “16.6.3/0.57.7” (tryed also with others)
- Realm JS SDK Version: “2.27.0-rc.3”
- Node or React Native: React Native
- Client OS & Version: Android (7.0)
- Which debugger for React Native: Chrome/React Native Debugger
About this issue
- Original URL
- State: closed
- Created 5 years ago
- Reactions: 3
- Comments: 31 (8 by maintainers)
@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.
@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
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 😛
@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
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
@kneth I sent a video to support and the complete source code of the App @ericjordanmossman
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.
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.warnthe listener is always triggered.