1

I'm just a beguinner in React-Native and i'm getting crazy every day with somo unexpected behaviors. Of course it is my fault, I'm still learning. Today i'm struggling to understand whats is goin on with my code.

I have a function that call an array of objects on my local storage. If I console the response from the promise it gives me a perfect array (i think). After that I setState and agin it appears correctly on my console. But, for my surprise, my list component doesn't render anything (and it's surely working as it was before).

I'll put my code attached:

/*FUNCTION FILE*/

getAllData: async () => {
    var data = [{id:null, address:null, name:null}];
    AsyncStorage.getAllKeys((err, keys) => {
      AsyncStorage.multiGet(keys, (err, stores) => {
        stores.map((result, i, store) => {
          // get at each store's key/value so you can work with it
          var key = store[i][0];
          var value = store[i][1];
          data[i] = {id: i, address: key, name: value}
        });
      });
    });
  return data},

//MAIN FILE (DAD COMPONENT)


import React, { Component } from 'react';
import { View, Text } from 'react-native';

import NewTagButton from '../components/NewTagButton';



import TagList from '../components/TagList';
import storage from '../functions/Storage';
import { Object } from 'core-js';
//import { TagPosition } from '../functions/GPSTag'

class TagsScreen extends Component {
  constructor(props) {
    super(props);
    
    this.state = {message:"",
                  pairedDevices: [], 
  
    };
  }

  componentDidMount () {
  var self = this;
  storage.getAllData() //tem que esperar a promise
        .then(function (devices) {
            self.setState({message: "Scaneamento finalizado."});
            self.setState({pairedDevices: devices});
            
        console.log("devices")
        console.log(devices)
        console.log("State")
        console.log(self.state.pairedDevices)
      })
        .catch(()=> console.log("Promise rejected"))
  }

  

  render() {
    
    return (
      <View>
        <Text>{this.state.message}</Text>
        <TagList  devices = {this.state.pairedDevices} origem = 'MyTag'/>
        <NewTagButton/>
      </View>
    );
  }
}

export default TagsScreen;

LIST COMPONENT THAT SHOULD RENDER THE LIST

import React from 'react';
import { View,  StyleSheet } from 'react-native';

import TagItem from '../components/TagItem'


const TagList = props => {

    const devicesList = props.devices;
    const origem = props.origem;

    //aqui a gente vai passar device a device para a page TagItem
    const items = devicesList.map((device) => 
    
        <TagItem 
            key = {device.address}       
            address = {device.address} 
            name = {device.name}
            origem = {origem}/>
     );


     return (
        <View key = {items} styles = {styles.container}>
          {items}
        </View>
      );
}



const styles = StyleSheet.create({
    container : {
        alignItems:'center',
        
  
    }
  })

export default TagList;

Thank you.

EDIT: I've put some logs between the components, the image is on a url because I don't have enough reputation haha

Console.Log

EDIT2:

I just created a silly button to set a state just with a string. After this state changing, the app render the list. For sure is a promise problem, can someone help me? =]

Matheus Molin
  • 11
  • 1
  • 3
  • I think you need to change `key = {items}` to something else so that it will re-render. – Colin Ricardo Nov 27 '18 at 23:16
  • Could you place some logs inside the taglist function to see if the function gets executed? Then it will be easier to isolate where the issue is. – Viktor W Nov 27 '18 at 23:22
  • Colin, i've tried to change the key but nothing happens. Actually, this component is utilized in one more place and it's working. I think the problem lays on object handling, maybe i'm make a confusion with Object indexes... – Matheus Molin Nov 27 '18 at 23:35

2 Answers2

1

Solution

The getAllKeys and multiGet of AsyncStorage are return promise. You need to use await for returning data is applied data from your logic.

getAllData = async () => {
    var data = [{id:null, address:null, name:null}];
    await AsyncStorage.getAllKeys(async (err, keys) => { // This is async function. 
      await AsyncStorage.multiGet(keys, (err, stores) => { // This is async function also. 
        stores.map((result, i, store) => {
          // get at each store's key/value so you can work with it
          var key = store[i][0];
          var value = store[i][1];
          data[i] = {id: i, address: key, name: value}
        });
      });
    });
    console.log(data); // Check your data. 
    return data
}

Why?

It will return data before finishing Promise function's promise without concern. Check the process number by order below.

getAllData = async () => {
    var data = [{id:null, address:null, name:null}]; // 1. 

    AsyncStorage.getAllKeys((err, keys) => { // 4. there is no async await decoration
      AsyncStorage.multiGet(keys, (err, stores) => { // 3. there is no async await decoration 
        stores.map((result, i, store) => {
          // get at each store's key/value so you can work with it
          var key = store[i][0];
          var value = store[i][1];
          data[i] = {id: i, address: key, name: value}
        });
      });
    });

    return data // 2. 
}

Thus, data has null when return from the getAllData function. After that, AsyncStorage's functions will be applied to the data variable.

Jeff Gu Kang
  • 4,749
  • 2
  • 36
  • 44
0

I've looked through your code a while and came up with a few things. Essentially, you're code isn't perfect, but I don't see anything that would break it like you are experiencing. Likely the issue is outside where you are looking. Either where TagsScreen is being called, or TagItem. Your edit with the console log isn't very clear where they are being called from as your example code hasn't been updated.

A couple of notes which may help you get on your way.

  • You can console log objects with console.log('myMessage', myObject); Unless you use JSON.stringify(), you're going to get [Object object] if you try to add an object to a string.
  • In your Promise.then, you don't want to call self.setState twice. This will render the app twice. You can see that in your console.log. Both of those can be in one setState; one object.
  • Calling your console.logs immediately after you set the state will make it look like the state isn't being updated. setState is async and will be fired after the event queue is cleared. If you want to track when the state has changed, use the react lifecycle function componentDidUpdate(oldProps){}
  • It would be helpful if you paired down your code so it can be run in jsFiddle. This is as far as I got, but it works perfectly. I have included lots of comments which explain what is exactly happening.

Steps I would take to debug:

  1. Add a console.log on deviceList inside TagList to ensure it is being called and the data is there.
  2. Ensure items is an array of TagItem jsx objects.
  3. Verify that the TagList html is being added to the DOM.
Nathan
  • 581
  • 2
  • 17