React-Native AsyncStorage: я получаю массив, но затем он становится только одним объектом массива

Я использую AsyncStorage для хранения и извлечения массива объектов. Структура массива такая:

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

Как видите, объекты в массиве сами содержат массивы, которые также содержат объекты.

Я храню массив следующим образом:

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

Кажется, это работает нормально. Затем я получаю данные следующим образом:

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

Кажется, это тоже работает нормально. У меня есть массив, хранящийся также как состояние. Итак, что я хочу сделать, это добавить объект в массив в состоянии, сохранить этот новый массив в AsyncStorage, а затем получить массив и вернуть этот новый массив в состояние. С хранением объекта вроде проблем нет.

Когда я извлекаю новый массив и console.log(JSON.parse(jsonValue)) внутри retrieveData, он печатает [object Object],[object Object],[object Object],[object Object]. Однако после того, как я позвоню const newData = retrieveData(), console.log(newData) напечатает только [object Object]. Я впервые использую AsyncStorage, поэтому, должно быть, я что-то не понимаю. Почему он возвращает только один объект вместо всего массива?

РЕДАКТИРОВАТЬ: Совместное использование всего кода компонента:

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;

Кроме того, я должен был упомянуть, что фактическая ошибка, которую я получаю, такова:

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


person T. Baer    schedule 30.07.2020    source источник
comment
Вы можете поделиться всем кодом компонента?   -  person D10S    schedule 30.07.2020
comment
Да, сейчас добавил.   -  person T. Baer    schedule 30.07.2020
comment
так что проблема в сообщении об ошибке или в асинхронном хранилище? что печатает «console.log) внутри «renderTracks»?   -  person D10S    schedule 30.07.2020
comment
Проблема заключается в ошибке, но она появилась только после того, как я представил AsyncStorage, поэтому я решил, что это должно быть причиной проблемы. console.log просто печатает переменную data для целей отладки.   -  person T. Baer    schedule 30.07.2020
comment
попробуйте обернуть 'data.map' ‹React.fragment›. вот так: return ‹React.Fragment›{data.map..........}‹/React.Fragment›   -  person D10S    schedule 30.07.2020
comment
Это не имело никакого эффекта   -  person T. Baer    schedule 30.07.2020
comment
Ошибка не появляется на обычном рендере. Это только после того, как я получил данные и установил их в состояние.   -  person T. Baer    schedule 30.07.2020
comment
Где вы печатаете console.log(newData)?   -  person D10S    schedule 30.07.2020
comment
В функции updateData()   -  person T. Baer    schedule 30.07.2020
comment
попробуйте изменить var newData = retrieveData(); в var newData = retrieveData().then(data => console.log(data). Он печатает то же самое?   -  person D10S    schedule 30.07.2020
comment
Это печатает [object Object],[object Object],[object Object],[object Object]   -  person T. Baer    schedule 30.07.2020


Ответы (2)


retrieveData является асинхронной функцией и, следовательно, возвращает Promise.
Произошло следующее: она не завершила получение данных, и, следовательно, newData получила 1 объект из всего массива.

Попробуйте изменить updateData следующим образом. :

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)
    };
}
person D10S    schedule 30.07.2020

Я разобрался с проблемой. Я извлекал данные с помощью AsyncStorage, а затем устанавливал эти данные в состояние примерно так:

var newData = asyncRetrieveDataFunction();

this.setState({state1: newData})

Однако, поскольку я объявил функцию retrieveData() асинхронной, она устанавливала состояние до завершения получения данных. Решение состояло в том, чтобы использовать ключевое слово then и изменить его на что-то вроде этого:

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

Это гарантирует, что данные были возвращены ДО присвоения их состоянию.

person T. Baer    schedule 30.07.2020
comment
к вашему сведению - это не потому, что ayncstorage является асинхронным, а потому, что вы декалировали retrieveData как асинхронный (чтобы использовать ожидание для asyncstorage) - person D10S; 30.07.2020