Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

UI doesn't update until tap on the screen when setState is called inside a realm listener callback

UPDATED

DESCRIPTION

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 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 update.

Thanks for reading this.

STEP 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

Since realm is not ready for 64-bit you must also be sure t compile only in 32bit in order to avoid app crashing when launched

use this code:

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"
  }
}

Some other strange things :

  • If I remote debug js to react native debugger (on Windows, but I guess is the same) the problem disappears.
  • The same thing happens on 3 different devices( 2 real, 1 emulator)
like image 983
DxW Avatar asked May 08 '19 16:05

DxW


People also ask

Why setState is not updating immediately?

setState , and React. useState create queues for React core to update the state object of a React component. So the process to update React state is asynchronous for performance reasons. That's why changes don't feel immediate.

Does setState Rerender immediately?

setState() will always lead to a re-render unless shouldComponentUpdate() returns false . To avoid unnecessary renders, calling setState() only when the new state differs from the previous state makes sense and can avoid calling setState() in an infinite loop within certain lifecycle methods like componentDidUpdate .

Why we should not update state directly?

So, when you mutate the state directly and call setState() with an empty object. The previous state will be polluted with your mutation. Due to which, the shallow compare and merge of two states will be disturbed or won't happen, because you'll have only one state now.

How do you wait for state to update?

Use the useEffect hook to wait for state to update in React. You can add the state variables you want to track to the hook's dependencies array and the function you pass to useEffect will run every time the state variables change. Copied!


1 Answers

In my case, I just stop the debugger (CMD+D), and that weird behavior went away. enter image description here

like image 185
Blarz Avatar answered Oct 15 '22 05:10

Blarz