T. Baer
T. Baer

Reputation: 69

React-Native AsyncStorage: I retrieve an array, but then it becomes only a single object of the array

I'm using AsyncStorage to store and retrieve an array of objects. The structure of the array is like this:

const tracks = [
    {
        title: title1,
        exercises: [
            {
                object1Info...
            },
            {
                object2Info...
            }
        ]
    },
    {
        title: title2,
        exercises: [
            {
                object1Info...
            }
        ]
    },
    {
        title: title3,
        exercises: [
            {
                object1Info...
            }
        ]
    }
]

As you can see, the objects in the array do themselves contain arrays, which again contain objects.

I'm storing the array like this:

const storeData = async (array) => {
    try {
        const stringifiedArray = JSON.stringify(array)
        await AsyncStorage.setItem('@tracks_array', stringifiedArray)
    } catch (e) {
        console.log("Error saving data")
    }
}

This seems to work fine. I then retrieve the data like this:

const retrieveData = async () => {
    try {
        const jsonValue = await AsyncStorage.getItem('@tracks_array');
        console.log('Parsed value: ' + JSON.parse(jsonValue)); //This prints 'Parsed value: [object Object],[object Object],[object Object],[object Object]'
        return jsonValue !== null ? JSON.parse(jsonValue) : null;
    } catch (e) {
        console.log("Error retrieving data")
    }
}

This seems to work fine as well. I have the array stored also as state. So what I want to do is add an object to the array in state, store that new array in the AsyncStorage, and then retrieve the array and set this new array back to state. Storing the object seems to have no problems.

When I retrieve the new array, and console.log(JSON.parse(jsonValue)) inside retrieveData, it prints [object Object],[object Object],[object Object],[object Object]. However after I call const newData = retrieveData(), console.log(newData) prints just [object Object]. This is my first time using AsyncStorage so I must be misunderstanding something. Why does it only return one object, instead of the whole array?

EDIT: Sharing the whole component code:

import {
    StyleSheet,
    ScrollView,
    View,
    Text
 } from 'react-native';
 import Modal from 'react-native-modal';
 import AsyncStorage from '@react-native-community/async-storage'
 import Track from './Track.js';
 import New from './New.js';

class Main extends Component {
    constructor(props) {
        super(props);

        this.state = {
            tracksData: tracks,
            newTrack: false,
            newExercise: false
        }

        storeData(this.state.tracksData);
    }

    renderTracks(data) {
        console.log('Main data = ' + data)
        return data.map((item, i) => {
            console.log('Item = ' + item)
            return (
                <Track key={i} data={item} />
            )
        });
    }

    render() {
        return (
            <ScrollView horizontal={true} style={styles.Main}>
                {this.renderTracks(this.state.tracksData)}
                <Track data={{title: 'NewTrack', exercises: 'NewTrack'}} newTrackBox={this.toggleTrackBox} />
                <Modal isVisible={this.state.newTrack} coverScreen={true}>
                    <New type={'track'} visible={this.toggleTrackBox} add={(name) => this.addTrack(name)}/>
                </Modal>
            </ScrollView>
        );
    }

    toggleTrackBox = () => {
        this.setState({
            newTrack: !this.state.newTrack
        })
    }

    addTrack = (name) => {
        this.setState({
            newTrack: false
        });

        var newTracks = this.state.tracksData;
        newTracks.push({title: name, exercises: []})

        console.log('newTracks = ' + newTracks)
        storeData(newTracks);
        
        this.updateData();        
    }

    updateData() {
        var newData = retrieveData();

        console.log('newData = ' + newData)
        
        setTimeout(() => {
            console.log('Retrieved data = ' + newData);
            if (newData) {
                this.setState({
                    tracksData: newData
                });
                console.log("Data updated");
                return true;
            } else {
                console.log("Data couldn't be retrieved");
                return false;
            }
        }, 5000)
    }
}

const storeData = async (value) => {
    try {
        const stringifiedArray = JSON.stringify(value)
        console.log('Value to store: ' + value)
        console.log('Stringified value to store: ' + stringifiedArray)
        await AsyncStorage.setItem('@tracks_array', stringifiedArray)
        //alert("Success saving data!")
    } catch (e) {
        console.log("Error saving data")
        alert("Error saving data");
    }
}

const retrieveData = async () => {
    try {
        const jsonValue = await AsyncStorage.getItem('@tracks_array');
        console.log('Stringified value retrieved: ' + jsonValue)
        console.log('Parsed value: ' + JSON.parse(jsonValue))
        return jsonValue !== null ? JSON.parse(jsonValue) : null;
    } catch (e) {
        console.log("Error retrieving data")
        alert("Error retrieving data");
    }
}

const tracks = [ //each member of this array is sent to a Track
    {
        title: 'Pull-up', // used in Track
        exercises: [ // each member of this array is sent to an Exercise by Track
            {
                name: 'Pull-up', // used in Exercise
                setStart: 2, // this and below used to calculate no of tiles and their contents, which are then sent to Tile
                setEnd: 3,
                repStart: 5,
                repEnd: 8,
                isInSeconds: false,
                inProgress: null,
                completed: true
            },
            {
                name: 'Weighted Pull-up',
                setStart: 3,
                setEnd: 3,
                repStart: 5,
                repEnd: 8,
                isInSeconds: false,
                inProgress: [3, 5],
                completed: false
            }
        ]
    },
    {
        title: 'Dip',
        exercises: [
            {
                name: 'Dip',
                setStart: 2,
                setEnd: 3,
                repStart: 5,
                repEnd: 8,
                isInSeconds: false,
                inProgress: null,
                completed: true
            }
        ]
    },
    {
        title: 'Squat',
        exercises: [
            {
                name: 'Pistol squat',
                setStart: 2,
                setEnd: 3,
                repStart: 5,
                repEnd: 8,
                isInSeconds: false,
                inProgress: [2, 8],
                completed: false
            }
        ]
    }
]

const styles = StyleSheet.create({
    Main: {
        flex: 1,
        flexDirection: 'row',
        backgroundColor: '#022763'
    }
})

export default Main;

Also, I should have mentioned, the actual error I'm getting is:

TypeError: undefined is not a function (near '...data.map...')

Upvotes: 0

Views: 876

Answers (2)

T. Baer
T. Baer

Reputation: 69

I've figured out the issue. I was retrieving data with AsyncStorage, then setting that data to the state something like this:

var newData = asyncRetrieveDataFunction();

this.setState({state1: newData})

However, because I declared the retrieveData() function as async, it was setting the state before the data had finished retrieving. The solution was to use the then keyword and change it to something like this:

asyncRetrieveDataFunction().then(data => this.setState({state1: data}));

This ensures that the data has been returned BEFORE assigning it to a state.

Upvotes: 1

D10S
D10S

Reputation: 1549

"retrieveData" is async function and hence returns a Promise.
What happened was it didn't finish retrieving the data and hence newData got 1 object out of all the array.

Try changing updateData like this:

updateData() {
    var newData = retrieveData().then(data => {
        console.log('newData = ' + newData)
        setTimeout(() => {
            console.log('Retrieved data = ' + newData);
            if (newData) {
                this.setState({
                    tracksData: newData
                });
                console.log("Data updated");
                return true;
            } else {
                console.log("Data couldn't be retrieved");
                return false;
            }
        }, 5000)
    };
}

Upvotes: 1

Related Questions