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

不问归期 提交于 2019-12-08 16:49:06

问题


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)

回答1:


UPDATE 2

setState({}) doesn't work when is inside the listener callback. I've just done a test changing the code in componentDidMount of Home.js, and in this way, it works.

It doesnt work because you are not binding the method that is calling it. it is beyond component context so setState is not there.

Do

   openRealmAndLogin = (realm_user) => {...}

instead of regular function as this one will bind the function to context. e.g. you can also bind it in constructor (but from what i've seen you are already doing something similar for other function - so better to keep it consistent)




回答2:


I would suggest that you change the key of the element; this will force it to reload whatever happens.

Ex:

{
  articoli.map(articolo => {
    const isLoved = connectedUserData.loved_articles.filtered("id = $0", articolo.id ).length
    const isLiked = connectedUserData.liked_articles.filtered("id = $0", articolo.id ).length
    const numCommenti = articolo.commenti.length

    return (
      <SchedaArticolo
        key={ `ALL_${articolo.id}_${isLoved}_${isLiked}_${numCommenti}` }
        articolo={articolo}
        isLoved={isLoved}
        isLiked={isLiked}
        numCommenti={numCommenti}
      />
    )
  })
}


来源:https://stackoverflow.com/questions/56044770/ui-doesnt-update-until-tap-on-the-screen-when-setstate-is-called-inside-a-realm

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!