Top Level Await for AsyncStorage in React Native - async-await

I am trying to implement top level await in my react native & expo environment so I can pull data from AsyncStorage and store it in a variable to use globally in my app.
I'm running the app on an android emulator. All I want to do is get the data and use it in my application, but the the variable always returns an empty object when called outside the async function. It seems to apply a value to the variable before the data from AsyncStorage is done loading.
async function getData(){
try {
const value = await AsyncStorage.getItem('redditInsights')
console.log('----------- getStore insights ---------')
const insights = JSON.parse(value)
console.log(insights) // -------------> returns desired data
return insights
}
catch (error) {
console.log(error)
}
}
const outsights = getData()
console.log("-------------------- outsights---------------------")
console.log(outsights) // ----------------------> returns empty object
I know I can pull the data inside an async function, but I can't use that data anywhere except inside the async function. I can't call a variable declared inside async outside of the function without it trying to apply a value to the variable before the data is even pulled.
Ideally I would simply store the data into a variable to use wherever I want without an async function like so, but this requires top level await support:
const value = await AsyncStorage.getItem('redditInsights')
const insights = JSON.parse(value)
I tried implementing top level await through numerous flags (--harmony-top-level-await, --experimental-repl-await, --experimental-top-level-await) at npm start like so: npm start --flag to no avail. I also upgraded to node v14.15.1 which is suppose to support top level await, but I still get an error.
How do I implement top level await in my react native & expo environment so I can use the data in my AsyncStorage
Or if anyone knows a better way to get my data from AsynStorage, I'm more than willing to try it out!!

The simple answer is this is not possible.
There's a more detailed answer about top level await in react-native here which says that React Native doesn't meet the requirements for top level await (uses node modules not ECMAScript native modules).
But your code sample has a mistake, you could declare a top level variable insights and assign the result of your async data to it. Your mistake is using the return value of getData you would need to declare a top level variable with let and then assign the result to it (so instead of return insights) you would do something like topScopeInsights = insights after a let topScopeInsights.
But, this doesn't make any sense, because you won't know if the data has arrived yet. The app will boot with your variable undefined and then you'll need to check if it has arrived or not. That's what promises are for.
So, back to my original answer, this is not possible. :-(

Related

Using `createMockClient` for testing non react code?

I have mixed application that uses Apollo for both React and non-react code.
However, I can’t find documentation or code examples around testing non-react code with the apollo client,not using MockedProvider. I did, however, notice that apollo exports a mock client from the testing directory.
import { createMockClient } from '#apollo/client/testing';
I haven’t found any documentation about this API and am wondering if it’s intended to be used publicly and, if not, what the supported approach is for this.
The reason I need this is simple: When using Next.js’ SSR and/or SSG features data fetching and actual data rendering are split into separate functions.
So the fetching code is not using React, but Node.js to fetch data.
Therefore I use apolloClient.query to fetch the data I need.
When trying to wrap a react component around that fetching code in a test an wrap MockedProvider around that the apolloClient’s query method always returns undefined for mocked queries - so it seems this only works for the useQuery hook?
Do you have any idea how to mock the client in non-react code?
Thank you for your support in advance. If you need any further information from me feel free to ask.
Regards,
Horstcredible
I was in a similar position where I wanted to use a MockedProvider and mock the client class, rather than use useQuery as documented here: https://www.apollographql.com/docs/react/development-testing/testing/
Though it doesn't seem to be documented, createMockClient from '#apollo/client/testing' can be passed the same mocks as MockedProvider to mock without useQuery. These examples assume you have a MockedProvider:
export const mockGetAssetById = async (id: Number): Promise<any> => {
const client = createMockClient(mocks, GetAsset)
const data = await client.query({
query: GetAsset,
variables: id,
})
return data
}
Accomplishes the same as:
const { data } = useQuery(
GetAsset,
{ variables: { id } }
)

Api calls in redux-saga crashing redux-devtools-extension

I'm trying to make some api calls using axios and redux-saga. This is nothing I haven't done before, and redux devtools usually handles this just fine. For some reason in the current application I'm working on, any actions that trigger a saga, which then make an api call, seems to crash my redux-devtools-extension. I know that redux-devtools-extension has always been a bit buggy, but I can't put my finger on why these actions might be crashing it. Here's a typical saga:
function* serverRefresh(): Generator {
try {
yield call(axios.get, "/api/restart"); // <------ crashes devtools extension
} catch (e) {
console.log(e);
}
}
function* watchServerRefresh(): Generator {
yield takeEvery(ActionTypes.RESTART_SERVER, serverRefresh);
}
export function* serverSagas(): Generator {
yield all([fork(watchServerRefresh)]);
}
Note that if I comment out the axios call, the extension works fine, properly registering actions. Other actions not coming from sagas have no problem. Switching from axios to fetch does not help. There's not a lot of logic here that might cause an infinite loop or trigger CPU overload - its a simple api call.
Here's how I set up my store and devtools extension:
function* rootSaga(): Generator {
yield all([fork(serverSagas), fork(campaignSagas)]);
}
const sagaMiddleware = createSagaMiddleware();
const rootReducer = combineReducers({ ...reducers });
export const store = createStore(
rootReducer,
composeWithDevTools(applyMiddleware(sagaMiddleware))
);
sagaMiddleware.run(rootSaga);
Why might non-saga actions, or saga non-api-call actions, be working fine, but pretty much any api call made with fetch or axios cause the devtools to freeze and crash?
The issue, which I didn't mention in my question, was that the redux store would freeze on any action that ocurred after action X. Action X was an action that stored a reference to a circular object in the store. (Its a leaflet map instance, for those interested.) While that action did not crash the devtools, and I'm even able to examine the leaflet map instance circular object with the devtools just fine, any action that ocurred after that circular object is stored in the store. So this really had nothing to do with sagas or api calls, but rather with keeping circular objects in the store, which we all know is a bad idea. I guess I can move that object over to a react context object instead.

Protractor, do I have to use then() after protractor calls?

Is it ok to write:
getFieldX().clear().sendKeys('abc');
or should I write:
getFieldX().clear().then( () => sendKeys('abc));
I'm totally confused about the Promise handling in protractor. clear() returns a promise, so I should use .then() afterwards, shouldn't I?
But I found examples with .then and some without.
Protractor itself has an example without .then():
https://www.protractortest.org/#/control-flow
Does Protractor has its own mechanism and resolves one after the other promise so there is no need for using .then() after a protractor call that returns a Promise?
And is the control flow of protractor only active in a spec?
When using .sendKeys() in a normal function I have to use .sendKeys().then(...) ?
This all depends on if you are using SELENIUM_PROMISE_MANAGER or not. As this is (has?) becoming deprecated, I would not use it. It should be set to false by default, but if you want to be sure you can add SELENIUM_PROMISE_MANAGER = false; to your conf file. The way that protractor has been moving is to use async/await, so your sendKeys function would look like:
let field = element(by.css('CSS.Selector'));
await field.clear();
await field.sendKeys('abc');
Because these are async functions, you will need to define your function properly, so a basic spec would look like:
describe('Demonstrating async/await',function(){
it('Should send some keys', async function(){
let field = element(by.css('CSS.Selector'));
await field.clear();
await field.sendKeys('abc');
});
});
The important difference there is that a function needs to be defined as async function(). As far as reading the code goes, await simply can be read as "Wait until promise resolved to move on". It does get a bit tedious and you feel like you write await before every line of code (you basically do), but I find it significantly better than .then() trees.

Creating a simple, basic page object in Nightwatch.js

Ok, so I've read up on the use of page_objects in nightwatch.js, but I'm still getting issues with it (which I'm convinced is due to something obvious and/or simple).
Using http://nightwatchjs.org/guide/#page-objects as the guide, I added the the file cookieremoval.js in my page_objects folder.
module.exports = {
elements: {
removeCookies: {
selector: '.banner_continue--2NyXA'
}
}
}
In my nightwatch.conf.js file I have;
page_objects_path: "tests/functional/config/page_objects",
And in my test script I have;
module.exports = {
"/cars/road-tax redirects to /car-tax/ ": browser => {
browser.url(browser.launch_url + browser.globals.carReviews)
.assert.urlEquals(browser.launchUrl + "/car-reviews/")
.waitForElementPresent('#cookieRemove', 3000)
.click('#cookieRemove')
.end();
},
};
However, when I run the test, I keep getting an error reading;
Timed out while waiting for element <#cookieRemove>
Any ideas why this is not working?
Many thanks
First of all, you never instantiated your page object. You're asking the browser object to search for an unknown element, that's why it's timing out. Your code should look something like this in your test script: var cookieRemoval = browser.page.cookieremoval(); then use this object to access those variables and functions in your page object. For example, if you wanted to access the remove cookie element, then you would do this cookieRemoval.click('#removeCookies');.
Secondly, you will have to know when to use the global browser object and when to use your page object. If you need to access something within your page object, obviously use the page object to call a function or access a variable. Otherwise, browser won't know the element you're looking for exists. Hope this help you out, I would definitely spend some more time learning about objects and specifically how they're used in nightwatch.js.

How do I add axios to react-redux-universal-hot-example starter kit?

So I picked react-redux-universal-hot-example as a starter kit.
I want to make ajax calls to 3rd party API.
(I want to use OpenWeatherMap API)
I want to use axios and react-promise middleware.
The first part installation is easy:
npm install --save redux-promise
npm install --save axios
Then I need to apply redux-promise middleware ... I think it goes into create.js somehow.
So I have several questions (all specific to react-redux-universal-hot-example) :
Is it the right way to do 3rd party calls using axios with redux-promise or the kit already have another way to do ajax requests?
Is create.js the right place to add redux-promise middleware?
Also, is there a concise syntax to apply multiple middlewares at the same time?
I am not 100% sure if you want to use react-promise or redux-promise. I am assuming the redux version. Which yea that create.js file is the right place to add it. You import a middleware from redux-promise and then add it to the lost of middlewares on line 9
import promiseMiddleware from 'redux-promise';
const middleware = [createMiddleware(client), reduxRouterMiddleware, promiseMiddleware];
So to answer your questions:
1. I am not 100% sure if the starter kit has a what to fetch 3rd party data. However using axios with redux-promise is exactly what I do.
2. Yes, see above on how.
3. Yep and that starter kit is showing one way which is creating an array of middlewares and then using the new spread operator it feed them into the applyMiddleware function. Which is a curried function that will then accept the createStore function. This is shown in the create.js file on line 21. Which is what the finalCreateStore will be during production.
4. The way I have been using this is with redux-actions which create Flux Standard Actions So..
// actionCreator
import { createAction } from 'redux-actions';
import { FETCH_DATA } from '../constants; // a file that exports constants
const fetchData = createAction(FETCH_DATA);
// reducer
const reducer = (state, action) => {
switch(action.type) {
case FETCH_DATA:
return {
...state,
fetchedData: action.payload.data
};
default:
return state;
}
}
// then dispatch the promise and not an action
store.dispatch(fetchData(axios.get('some_route')));
Then redux-promise 'intercepts' the promise and will resolve it. Once the promise resolves the action is then re-dispatched with the results in the payload. With axios you will get back an object that had the data you want as a property of that object. Which is shown is in the reducer with action.payload.data
A project I am working on that has this as an example.
Please not the actions have been wrapped with the dispatch call so all I have to do is call the actionCreators directly to get the same result as dispatching. This is explained in redux docs on bindActionCreators.
actionCreator
reducer
Component is where I dispatch the action
Where I hook up the middleware <-- similar to your create.js file
Again by dispatching the promise redux-promise intercepts(my way of understanding) the promise, resolves it and then dispatches the same action again but with the results of the promise on the payload property of the action object.

Resources