Describe the bug
After newest update, DataStore queries don't do anything on the first load of the react-native application. Only on the second load, it starts to work properly.
To Reproduce
Steps to reproduce the behavior:
Expected behavior
Expecting some data on the first load, as it worked before.
Code Snippet
Standard DataStore.query(SomeDataToQuery)
What is Configured?
It worked on this version:
"@aws-amplify/api": "3.1.12",
"@aws-amplify/auth": "3.2.9",
"@aws-amplify/cache": "3.1.12",
"@aws-amplify/core": "3.2.9",
"@aws-amplify/datastore": "2.1.2",
"@aws-amplify/pubsub": "3.0.13",
"@aws-amplify/storage": "3.2.2",
Now it doesn't:
"@aws-amplify/api": "3.1.14-unstable.2",
"@aws-amplify/auth": "3.2.11-unstable.2",
"@aws-amplify/cache": "3.1.14-unstable.2",
"@aws-amplify/core": "3.3.1-unstable.2",
"@aws-amplify/datastore": "2.2.1-unstable.2",
"@aws-amplify/pubsub": "3.0.15-unstable.2",
"@aws-amplify/storage": "3.2.4-unstable.2",
Additional context
Few other guys are getting the same issue, you can find more on discord channel for amplify-js.
Running a react-native application.
Forgot to mention, I tried to use your fancy await DataStore.start() before first queries, didn't seem to do anything.
I'll tag along, same problem here on react native too.
Hi @Darapsas and @nubpro
When enabling syncing to the cloud on the DataStore, queries you run might yield empty or incomplete results if the initial sync process hasn't completed or there is not enough data synced locally at the time of the query. In this scenario, you can either wait for the sync process to finish (by listening to the 'ready'
event on the 'datastore'
Hub channel) or you can observe a model until you accumulate enough data to render your UI.
Below you'll find these two approaches exemplified:
Waiting for the sync process to finish
import React, { useEffect, useState } from "react";
import { Amplify, Hub } from "@aws-amplify/core";
import { DataStore, Predicates } from "@aws-amplify/datastore";
import awsConfig from "./aws-exports";
import { Note } from "./models";
Amplify.configure(awsConfig);
function App() {
const [notes, setNotes] = useState([] as Note[]);
useEffect(() => {
// Create listener that will stop observing the model once the sync process is done
const removeListener = Hub.listen("datastore", async (capsule) => {
const {
payload: { event, data },
} = capsule;
console.log("DataStore event", event, data);
if (event === "ready") {
const notes = await DataStore.query(Note, Predicates.ALL, {
page: 0,
limit: 15,
});
setNotes(notes);
}
});
// Start the DataStore, this kicks-off the sync process.
DataStore.start();
return () => {
removeListener();
};
}, []);
return (
<div>
<ul>
{notes.map((note) => (
<li key={note.id}>{note.id}</li>
))}
</ul>
</div>
);
}
Observing a Model until enough data is available
import React, { useEffect, useState } from "react";
import { Amplify, Hub } from "@aws-amplify/core";
import { DataStore, Predicates } from "@aws-amplify/datastore";
import awsConfig from "./aws-exports";
import { Note } from "./models";
Amplify.configure(awsConfig);
function App() {
const [notes, setNotes] = useState([] as Note[]);
// Number of notes we want to show in our initial render
const limit = 15;
useEffect(() => {
let subscription: any = undefined;
// Create listener that will stop observing the model once the sync process is done
const removeListener = Hub.listen("datastore", (capsule) => {
const {
payload: { event, data },
} = capsule;
console.log("DataStore event", event, data);
if (event === "ready") {
if (subscription) {
removeListener();
}
}
});
// Async IIFE for more convenient async/await syntax
// see: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/async_function#Description
(async () => {
const notes = await DataStore.query(Note, Predicates.ALL, {
page: 0,
limit: limit,
});
if (notes.length === limit) {
setNotes(notes);
return;
}
// If we didn't get enough data from the initial query, observe the model until we do
subscription = DataStore.observe(Note).subscribe(({ element, ...x }) => {
notes.push(element);
if (notes.length === limit) {
subscription.unsubscribe();
setNotes(notes);
}
});
})();
return () => {
// We clean up the hub listener and the observe subscription
removeListener();
if (subscription) {
subscription.unsubscribe();
}
};
}, []);
return (
<div>
<ul>
{notes.map((note) => (
<li key={note.id}>{note.id}</li>
))}
</ul>
</div>
);
}
export default App;
@manueliglesias When I use the "Observing a Model until enough data is available" method you laid out above, I end up with no data. The only difference in my code is that I pass an ID to the observe method.
So this doesn't seem to be working:
const id = '69ddcb63-7e4a-4325-b84d-8592e6dac07b';
subscription = DataStore.observe(Note, id).subscribe(({ element, ...x }) => {
notes.push(element);
if (notes.length === limit) {
subscription.unsubscribe();
setNotes(notes);
}
});
When I remove the ID it works. I cleared the DataStore before testing each time.
I've created a new issue: https://github.com/aws-amplify/amplify-js/issues/6003
Hi @mdoesburg
I went through my code again and try it in fresh apps and it worked as expected, do you have a codesandbox with your code or similar where I can take a closer look at it?
@manueliglesias Yeah sorry about that, it seems like your code does work. It doesn't work when I pass an ID in. I've created a new issue here: https://github.com/aws-amplify/amplify-js/issues/6003
Hello there, @manueliglesias,
So this morning I have upgraded packages to the newest "stable" versions:
"@aws-amplify/api": "3.1.15",
"@aws-amplify/auth": "3.2.12",
"@aws-amplify/cache": "3.1.15",
"@aws-amplify/core": "3.3.2",
"@aws-amplify/datastore": "2.2.2",
"@aws-amplify/pubsub": "3.0.16",
"@aws-amplify/storage": "3.2.5",
"amazon-cognito-identity-js": "4.3.1",
And then I tried your code snippet (in theory everything should work, it looks very clean) with Hub.listen (the first one) and then my terminal spits this into my face:
My code snippet, based on yours:
useEffect(() => {
// Create listener that wil stop observing the model once the sync process is done
const removeHubListener = Hub.listen('datastore', async (capsule) => {
const {
payload: { event, data },
} = capsule
console.log('DataStore event:::::::::::::::::::::::::::: ', event, data)
if (event === 'ready' ) {
console.log(
'I am ready as hell ---------------------------------------'
)
}
})
console.log('Trying to start DataStore: ')
DataStore.start()
return () => {
removeHubListener()
}
}, [])
Maybe you have any ideas?
@manueliglesias Ok, so I think I found the culprit to why I am getting mixed results. See the following example:
import React, { useCallback, useEffect, useState } from 'react';
import { Button, Text, View } from 'react-native';
import { DataStore } from '@aws-amplify/datastore';
import { Hub } from '@aws-amplify/core';
import { Contact } from '~/models';
const DataStoreScreen = () => {
const [data, setData] = useState([]);
useEffect(() => {
const removeListener = Hub.listen('datastore', async ({ payload }) => {
console.log(payload.event, payload.data);
if (payload.event === 'ready') {
console.log('DataStore ready');
dataQuery();
}
});
console.log('Starting DataStore');
DataStore.start();
return () => removeListener();
}, [dataQuery]);
const dataQuery = useCallback(async () => {
console.log('Querying data...');
const _data = await DataStore.query(Contact);
console.log(_data);
setData(_data);
}, []);
const clear = () => {
DataStore.clear();
setData([]);
};
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Data</Text>
<Text>{JSON.stringify(data)}</Text>
<Button title="Query" onPress={dataQuery} />
<Button title="Clear" onPress={clear} />
</View>
);
};
export default DataStoreScreen;
I get data when I query, but after clearing the DataStore queries stop working. Hard reloading after clearing shows the data again, but my app clears the DataStore when a user logs out, so this issue is problematic for me.
@mdoesburg There's a PR that might help you, #6010
Once it is merged, you will be able to try it on @unstable
@manueliglesias I tried @unstable
, and get no events when listening to 'datastore' on the Hub, after running DataStore.start();
. Even if I just observe, and don't run start, I get no events.
@manueliglesias DataStore version: 2.2.3-unstable.1
@mdoesburg The publish to npm just finished, try again, these are the versions:
Successfully published:
- [email protected]+b5347ab62
- @aws-amplify/[email protected]+b5347ab62
- @aws-amplify/[email protected]+b5347ab62
- @aws-amplify/[email protected]+b5347ab62
- @aws-amplify/[email protected]+b5347ab62
- @aws-amplify/[email protected]+b5347ab62
- @aws-amplify/[email protected]+b5347ab62
- @aws-amplify/[email protected]+b5347ab62
- @aws-amplify/[email protected]+b5347ab62
- @aws-amplify/[email protected]+b5347ab62
- @aws-amplify/[email protected]+b5347ab62
- [email protected]+b5347ab62
- [email protected]+b5347ab62
- [email protected]+b5347ab62
- [email protected]+b5347ab62
- [email protected]+b5347ab62
- @aws-amplify/[email protected]+b5347ab62
- @aws-amplify/[email protected]+b5347ab62
- @aws-amplify/[email protected]+b5347ab62
- @aws-amplify/[email protected]+b5347ab62
- @aws-amplify/[email protected]+b5347ab62
- @aws-amplify/[email protected]+b5347ab62
- @aws-amplify/[email protected]+b5347ab62
- @aws-amplify/[email protected]+b5347ab62
- @aws-amplify/[email protected]+b5347ab62
@manueliglesias Thank you, it works now. Keep up the great work.
My use case for data sync notifications is so I can warn users before logging out if the data they created while offline has not yet been synced with the cloud. Is there a chance that use cases like this will be added to the Amplify documentation? How can I check if theres still items that were created offline that haven't been synced? Like which event do I have to listen to on the Hub?
@manueliglesias Nevermind I figured out I can listen to the outboxStatus event:
outboxStatus {"isEmpty": false}
Most helpful comment
Hi @Darapsas and @nubpro
When enabling syncing to the cloud on the DataStore, queries you run might yield empty or incomplete results if the initial sync process hasn't completed or there is not enough data synced locally at the time of the query. In this scenario, you can either wait for the sync process to finish (by listening to the
'ready'
event on the'datastore'
Hub channel) or you can observe a model until you accumulate enough data to render your UI.Below you'll find these two approaches exemplified:
Waiting for the sync process to finish
Observing a Model until enough data is available