Fetching data on IOS react redux - ios

I'm building my first application using react and redux, and for some reason, the structure of the data received on a Async action is behaving different on the IOS (safari).
This action makes the fetch (cross-fetch) request:
export function fetchTransactions() {
return (dispatch) => {
dispatch(requestTransactions());
return fetch('/getTransactions', {
method: 'GET',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
Cache: 'no-cache',
},
credentials: 'same-origin',
})
.then(
response => response.json(),
error => console.log('An error occurred.', error),
)
.then(json => dispatch(receiveTransactions(json.dados)));
};
}
And the receive action is:
export const RECEIVE_TRANSACTIONS = 'RECEIVE_TRANSACTIONS';
export function receiveTransactions(data) {
return {
type: RECEIVE_TRANSACTIONS,
data,
};
}
Reducer
case 'REQUEST_TRANSACTIONS':
return Object.assign({}, {...state}, {isFetching: true});
case 'RECEIVE_TRANSACTIONS':
return Object.assign({}, {...state}, {data: action.data}, {isFetching: false});
Now I'll show you the results on Firefox running on linux:
It works as expected, the transaction data is present on the array called data.
Here is a print screen from a Safari emulator:
As you can see, the are duplicate fields and other fields. This behavior is causing the application to not work correctly. Any ideas? Thank you very much and sorry about the English thing.
#edit-------------------------------------------
Running the application on other emulator, results in a good state but the data isn't displayed on the table as you can see bellow:
As you can see, there are 100 data objects in the array, and nothing on the table, this is not happening on firefox or chrome.

Related

POST request doesn't work on iOS devices in IONIC app

I tried to make post-request to my server from iOS device (iOS emulator inside Xcode). I'm using ionic 5 and VueJS and #capacitor-community/http.
So my code looks like:
methods: {
async submitForm(){
const form_data = this.$store.getters.my_data;
const options = {
url: 'https://my_domain.com/api/page.php',
headers: {"Content-Type": "application/json"},
data: form_data,
};
await Http.post(options)
.then(res => {
if (res.status === 200) {
console.log(res.data)
}
})
.catch((err) => {
console.log(err)
});
}
I have no submit buttons and I'm not using prevent in the method
When I run my project in the web to testing - I have no problems. GET and POST requests works fine (when testing as web-app I have to remove headers: {"Content-Type": "application/json"} for prevent CORS problems).
So, next I run:
ionic build
npx cap copy
Then I try android emulator (inside android-studio). And my app also works great (all types of requests). Builded android app works perfect on real devices too.
But when it comes to iOS... GET request works fine, as expected. But when I press button which initialize POST request - nothing happens! No errors... No alerts... No logs... Just nothing. My backend doesn't get any request. I have no idea what to do.
There are lines in my Info.plist:
<key>WKAppBoundDomains</key>
<array>
<string>www.my_domain.com</string>
<string>https://my_domain.com</string>
</array>
TransportSecurity flag set as TRUE:
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
I tried use native fetch, but the result is same - nothing happens.
So I think it is kind of problems with iOS, but I haven't enough knowledges to understand what the problem is.
So, a few days I tried to realize what's wrong with my code.
Finally I found some logs which says:
Unhandled Promise Rejection: DataCloneError: The object can not be cloned.
It means that something wrong with my data I trying to send.
And the reason is:
const form_data = this.$store.getters.my_data;
This line returns not a json object but some Proxy object. The behavior is pretty similar to json: you can use form_data.name or any other values, but swift for some reasons can't use this object.
So, we need just to create new json object for using in post body:
async submitForm(){
const form_data = this.$store.getters.my_data;
const body_data = {};
for (const [key, val] of Object.entries(form_data)) {
body_data[key] = val;
}
const options = {
url: 'https://my_domain.com/api/page.php',
headers: {"Content-Type": "application/json"},
data: body_data,
};
await Http.post(options)
.then(res => {
if (res.status === 200) {
console.log(res.data)
}
})
.catch((err) => {
console.log(err)
});
}
And this code works fine
P.S. be sure that all you values in body_data are standard JS types
I had the same Issue:
Worked in Android and Browser but not on IOS.
VueJS Composition API Uses Some Poxy on Reactive Variables.
Convert it into a JSON Object first - Then it works for me.
const bodyJson = JSON.stringify(bodyParameters);
const options = {
url: `https://my_domain.com/api/page.php`,
headers: { "Content-Type": "application/json" },
data: bodyJson,
};

Disable relayjs garbage collection

Is there a way to disable the relayjs garbage collection (version 5.0.0 or 6.0.0)?
We are still using relayjs classic and it caches all data in a session. This makes loading previous pages fast while fetching new data. In relayjs 5.0.0 they have a dataFrom on the QueryRenderer that can be set to "STORE_THEN_NETWORK" which will try the relay cache store first and fetch from the network, just like rejay classic. Except that the newer versions of relay uses a garbage collection feature to remove data that is not currently used. This makes almost all pages fetch data from the network.
I managed to get this working. The key thing here is the environment.retain(operation.root); which will retain the objects in the cache.
Then in the QueryRenderer use the fetchPolicy="store-and-network".
See my full Relay Environment file below.
import {Environment, Network, RecordSource, Store} from 'relay-runtime';
function fetchQuery(operation, variables) {
const environment = RelayEnvironment.getInstance();
environment.retain(operation.root);
return fetch(process.env.GRAPHQL_ENDPOINT, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
credentials: 'include',
body: JSON.stringify({
query: operation.text,
variables
})
}).then(response => {
return response.json();
});
}
const RelayEnvironment = (function() {
let instance;
function createInstance() {
return new Environment({
network: Network.create(fetchQuery),
store: new Store(new RecordSource())
});
}
return {
getInstance: function() {
if (!instance) {
instance = createInstance();
}
return instance;
}
};
})();
export default RelayEnvironment;
Also got this from the Relay Slack Channel. Haven't tried it yet.
const store = new Store(new RecordSource());
(store as any).holdGC(); // Disable GC on the store.

workbox offline response from IDB instead of cache

I am building an vueJs application with a service worker. I decided to use Workbox with an InjestManifest method to had my own routes.
on fetch when online :
1- answer with the network
2- wrtting body to IDB (through localforage)
3- send back the response
here everything is working perfectly, the sw intercepts the fetch and come back with an appropirate response, IDB contains rigth details.
response sent back to fecth when online:
Response {type: "cors", url: "http://localhost:3005/api/events", redirected: false, status: 200, ok: true, …}
the issue is when I go offline.
my intention id to connect to Locaforage and retrieve the content and build a response.
The issue is that this response is not considered as appropriate by Fetch who then reject it. Console.log confirms that the .catch in sw is working but it looks like the response it sends is rejected.
here is the console.log of the response I am sending back to fetch when offline;
Response {type: "default", url: "", redirected: false, status: 200, ok: true, …}
I do not know if fetch is not happy becasue the url of the repsonse is not the same as on the request but workbox is supposed to allow responding with other resposnes than the ones coming from cache or fetch.
here is the code
importScripts('localforage.min.js')
localforage.config({
name: 'Asso-corse'
})
workbox.skipWaiting()
workbox.clientsClaim()
workbox.routing.registerRoute(
new RegExp('https://fonts.(?:googleapis|gstatic).com/(.*)'),
workbox.strategies.cacheFirst({
cacheName: 'googleapis',
plugins: [
new workbox.expiration.Plugin({
maxEntries: 30
})
]
})
)
workbox.routing.registerRoute( new RegExp('http://localhost:3005/api/'), function (event) {
fetch(event.url)
.then((response) => {
var cloneRes = response.clone()
console.log(cloneRes)
cloneRes.json()
.then((body) => {
localforage.setItem(event.url.pathname, body)
})
return response
})
.catch(function (error) {
console.warn(`Constructing a fallback response, due to an error while fetching the real response:, ${error}`)
localforage.getItem(event.url.pathname)
.then((res) => {
let payload = new Response(JSON.stringify(res), { "status" : 200 ,
"statusText" : "MyCustomResponse!" })
console.log(payload)
return payload
})
})
})
workbox.precaching.precacheAndRoute(self.__precacheManifest || [])
I am really stuck there as all documentation on workbox relates to leveraging cache. I am leveraging localforage as it supports promises which is what is required to make offline capability working.
Thanks
Your catch() handler needs to return either a Response object, or a promise for a Response object.
Adjusting the formatting of your sample code a bit, you're currently doing:
.catch(function (error) {
console.warn(`Constructing a fallback response, due to an error while fetching the real response:, ${error}`)
localforage.getItem(event.url.pathname).then((res) => {
let payload = new Response(JSON.stringify(res), { "status" : 200 , "statusText" : "MyCustomResponse!" })
console.log(payload)
return payload
})
})
Based on that formatting, I think it's clearer that you're not returning either a Response or a promise for a Response from within your catch() handler—you're not returning anything at all.
Adding in a return before your localforage.getItem(...) statement should take care of that:
.catch(function (error) {
console.warn(`Constructing a fallback response, due to an error while fetching the real response:, ${error}`)
return localforage.getItem(event.url.pathname).then((res) => {
let payload = new Response(JSON.stringify(res), { "status" : 200 , "statusText" : "MyCustomResponse!" })
console.log(payload)
return payload
})
})
But, as mentioned in the comments to your original question, I don't think that using IndexedDB to store this type of URL-addressable data is necessary. You can just rely on the Cache Storage API, which Workbox will happily use by default, when storing and retrieving JSON data obtained from an HTTP API.

cannot get extjs grid to populate with proxy data call

I have gone from incorporating extjs in my original asp.net application which worked when hardcoding any data stores and binding them to the charts/grids. When I tried proxy url calls or even fetching the data from code behind and wrapping in json I still do not get the data into the grid. So I gave up and went with extjs and nodejs and still using mongodb; this worked perfectly but I still have to learn to create a better UI using express/jade etc which is a different project now. But then I came across using MVC with extjs and with a sample project tried the same thing (the sample had hardcoded data) and I cannot for the life of me get it to display the data.
Ext.require([
'Ext.grid.*',
'Ext.data.*',
'Ext.util.*',
'Ext.state.*'
]);
Ext.onReady(function () {
Ext.QuickTips.init();
// setup the state provider, all state information will be saved to a cookie
Ext.state.Manager.setProvider(Ext.create('Ext.state.CookieProvider'));
Ext.define('User', {
extend: 'Ext.data.Model',
fields: [
{ name: 'username', type: 'string' }
]
});
Ext.define('UserStore', {
extend: 'Ext.data.Store',
model: 'User',
autoload: true,
proxy: {
type: 'ajax',
url: '/dashboard.aspx/getDBData',
reader: {
type: 'json',
root: 'users'
},
listeners:
{
exception: function (proxy, response, operation) {
Ext.MessageBox.show(
{
title: 'REMOTE EXCEPTION',
msg: operation.getError(), icon: Ext.MessageBox.ERROR, buttons: Ext.Msg.OK
});
}
}
}
});
var myStore = Ext.getStore('UserStore');
the url I am including here is the codebehind function that I initially tried which accesses the mongodb and returns json result. Not working.
Now from the extjs node.js application I have results coming into localhost:3000/userlist which returns a list from mongodb and displays it as follows:
extends layout
block content
h1.
User List
u1
each user, i in userlist
li
a(href="mailto:#{user.email}")= user.username
Now would it be possible to use the same server and call the base url and then change the route.js file to return the mongodb json result or call the mongodb localhost:27017 and get a result. Really confused here
exports.index = function(db) {
return function(req, res) {
var collection = db.get('usercollection');
collection.find({},{}, function(e,docs){
res.render('userlist', {
"userlist" : docs
});
});
};
};
EDIT:
First thing I realized from asp.net perspective was that I was not calling a webservice just a codebehind method. Any comments will still be appreciated.
EDIT 2:
{"connTime":null,"userName":"101591196589145","clientName":null,
"feedUrl":null,"dconnTime":null,"errMessage":null,"ip":null}
You have identified a root in your store as 'users'
reader: {
type: 'json',
root: 'users'
},
But there is no root in your returned json such as:
{"users":[{"connTime":null,"userName":"101591196589145","clientName":null,
"feedUrl":null,"dconnTime":null,"errMessage":null,"ip":null}]}

jQuery.ajax reloads page instead of performing ajax request in Safari on iOS

I have a function being called on a page on a local apache instance (/test) which calls a subpage (/test/info) with jQuery.ajax and correctly makes an AJAX call and dynamically loads the content from the response on my desktop in FF, Safari, Chrome, but in the iOS emulator, no call is made and the page is refreshed.
window.getInfo = function ( event ) {
console.log('1) prior to $.ajax');
$.ajax({
url: 'http://localhost/test/info',
dataType: 'html',
beforeSend: function(xhr) {
console.log('2) beforeSend');
},
success: function(data, textStatus) {
console.log('3) success');
if ( textStatus == 'success' ) {
// doing stuff with data
}
}
}).always( function() { console.log('4) always'); });
};
From the desktop browsers all of the logs are printed and my apache server reports a request at /test, but on Safari in the iOS emulator, only the '1) prior to $.ajax' and '2) beforeSend' logs are printed and the next request made to apache is for /test.
Does anyone have any idea what is happening here, and how to make iOS behave itself?
UPDATE: When I add the async: false attribute to the ajax call, all the logs are printed, and the request is made, so that basically fixed the issue; however the page still reloads which I believe is a different issue related to event propagation on iOS.
All that is needed to make this work is a return false; from the handler function. See event.preventDefault() vs. return false for a more complete explanation, but basically "return false from within a jQuery event handler is effectively the same as calling both e.preventDefault and e.stopPropagation on the passed jQuery.Event object."
So a fully functional version of the above code is:
getInfo = function() {
$.ajax({
url: '/test/info',
dataType: 'html',
success: function(data, textStatus) {
if ( textStatus == 'success' ) {
// doing stuff with data
}
}
});
return false;
};
// The order of vv that string may be important
$(document).on('click touchend', '.getInfo', getInfo );

Resources