Chrome and Edge hang on React page load for only some users, should I change my useEffect strategy? - reactjs

My ReactJS project displays a simple page consisting of a header section with project title, version and a few nav links, then a table of about 200 results as the main content of the page.
The initial page loads for everyone and the components appear as expected, but on page load (I believe this is when the useEffect hook kicks in) some users report the page becoming un-responsive and no matter how long it is left, it never finishes. This has been reported in both Chrome and Edge by 5 different users across a site of 200+ users, the majority have no issues despite running the exact same hardware and connection.
On page load, I expect the title, version and table contents (plus a few other variables) to be populated and automatically updated since these are in state, and for most users, this works as expected.
Below is my useEffect()
useEffect(() => {
// Update all initial values
fetchLastUpdated();
fetchVersion();
fetchUsername();
fetchUpcomingFilterOptions();
fetchLongCustomerNames();
fetchConfigs();
fetchUpcomingResults() // This will be displayed as rows
const job = document.getElementById("job")
if ( !!job ) {
job.addEventListener("keyup", function(event) {
if (event.key === "Enter") {
submitForm()
}
});
}
// Find environment for API links: testing/pre-release, testing/QA, flx
const url = window.location.href
if ( url.includes('localhost') ) {
setEnvironment("testing/pre-release")
} else if ( url.includes('testing/pre-release') ) {
setEnvironment("testing/pre-release")
} else if ( url.includes('testing/QA') ) {
setEnvironment("testing/QA")
} else if ( url.includes('flx') ) {
setEnvironment("flx")
}
}, [])
Below an example of an API call from useEffect
const fetchConfigs = () => {
axios({
method: "get",
url: "http://myURL/" + environment + "/WITracker/public/api/myConfigs",
config: { headers: {
'Access-Control-Allow-Origin': '*',
"Content-Type": "multipart/form-data"
}}
})
.then(function (response) {
setConfigs(response.data);
})
.catch(function (response) {
console.log("Failed to fetch configs!");
addNotification("Unable to fetch configs", "Retry in progress...")
})
}
When remote accessing the users with troubles loading the page, I asked that they each try the alternative browser: Chrome -> Edge or Edge -> Chrome and in each case this resolved the issue. I found this strange as I would have expected the same browser to be causing the same behaviour each time across the users.
I would like to make sure that the page reliably loads for all users regardless of their browser preference. I'm at a bit of a loss trying to find out why only some users are getting unresponsive errors so any possible solutions or suggestions of what to try are welcome!
Possible workaround?
I'm not sure that I have set up my useEffect the correct way using best practices. I'm thinking of adding a slight delay to the API calls, since the page loads the components without issue, and once the delay is up, to synchronously make each of the calls, giving the browser more of a chance to process the smaller chunks of work rather than all at once... please can somebody let me know their thoughts on this?
e.g. Something similar to the below theory?
useEffect(async () => {
// Some delay here, with loading screen
wait(1000) //custom function to wait?
// ...then, update all initial values
await fetchLastUpdated();
await fetchVersion();
await fetchUsername();
await fetchUpcomingFilterOptions();
await fetchLongCustomerNames();
await fetchConfigs();
await fetchUpcomingResults()
...
Thanks in advance

Related

Using ApolloClient pagination API results in requests, even if all page content is already in cache

I am using the ApolloClient core pagination API approach to accumulate paginated requests in a merge function and the repaginate them with a read function: https://www.apollographql.com/docs/react/pagination/core-api
This all works, but now there is a request for each page, even the ones that are already in the cache.
Which defeats the whole purpose when I'm repaginating!
I'm using the default fetchStrategy, cache-first.
If all requested data is present in the cache, that data is returned. Otherwise, Apollo Client executes the query against your GraphQL server and returns that data after caching it.
I wonder how ApolloClient checks that all requested data is in the cache with the pagination implementation.
Because right now (and the docs seems to rely on this) it always does the request, even when the keyArgs match and the data is in the cache.
Does someone know what causes this and how I can customize this cache-first strategy to check if all the items of the requested page are already in the cache?
Here is my code, in case that helps for context or if I'm just doing something wrong:
typePolicies: {
Query: {
fields: {
paginatedProductTracking: {
// Include everything except 'skip' and 'take' to be able to use `fetchMore`
// and repaginate when reading cache
// (essential for switching between desktop pagination and mobile lazy loading
// without having to refetch)
keyArgs: (args) => JSON.stringify(omit(args, ['query.skip', 'query.take'])),
merge: (existing, incoming, { args }) => {
if (!existing) {
return incoming;
}
if (!incoming) {
return existing;
}
const data = existing.paginatedData;
const newData = incoming.paginatedData;
return {
...existing,
// conservative merge that is robust against pages being requested out of order
paginatedData: [
...data.slice(0, args?.query.skip || 0),
...newData,
...data.slice((args?.query.skip || 0) + newData.length),
],
};
},
},
},
},
},
const [pageSize, setPageSize] = useState(100);
const [page, setPage] = useState(0);
const skip = page * pageSize;
const query = {
filter,
aggregationInterval,
order,
skip,
take: pageSize,
search: search ? values : null,
locations: currentLocations.length > 0 ? currentLocations.map((location) => location.id) : undefined,
};
const { data, loading, fetchMore } = useProductTrackingAggregatedDataQuery({
variables: {
query,
},
});
onPageChange={async (newPage) => {
await fetchMore({
variables: {
query: {
...query,
skip: newPage * pageSize,
},
},
});
setPage(newPage);
}}
I was recently faced with the exact same issue and had everything implemented in the way the official documentation illustrates until I stumbled upon this issue which is still open so I'm guessing this is still how the fetchMore function actually behaves to date. So #benjamn says that:
The fetchMore method sends a separate request that always has a fetch policy of no-cache, which is why it doesn't try to read from the cache first.
This being the case, fetchMore is only useful if you are implementing an endless scroll sort of pagination where you know beforehand that the new data is not in the cache.
In the pagination documentation it also states that:
If you are not using React and useQuery, the ObservableQuery object returned by client.watchQuery has a method called setVariables that you can call to update the original variables.
If you change the variables to your query it will trigger your read function implementation. And if the read function finds the data within existing it can return them or return undefined which will in turn trigger a network request to your graphql server to fetch the missing data, which will trigger your merge function to merge the data in the desired way, which will again trigger the read function which will now be able to slice the data you requested according to your { args } out of your existing and return them, which will finally trigger your watched ObservableQuery to fire and your UI to be updated.
Now, this approach is counter intuitive and goes against the "recommended" way of implementing pagination, but contrary to the recommended way this approach actually works.
I was unable to find anything that would prove my conclusions about fetchMore to be wrong, so if any Apollo client guru happens to stumble upon this please do shed some light into this. Until then the only solution I can offer is working with setVariables instead of fetchMore.
Keep in mind that you will need to implement a read function along with your merge. It will be responsible for slicing your cached data and triggering a network request by returning undefined if it was unable to find a full slice.

update state of the parent screen if an update occurs in current screen

I am using react-navigation 6 and react-native CLI, to make a chat application. I want to achieve this feature which is common in every chat application that when we send a message to someone, and go back to homescreen of the app (where all conversations are listed), the last message sent, can be seen.
Like if I sent message and pressed the back button, it will navigate me to home screen where all my conversations are, and it should update the conversation where I sent the message.
I have tried route.params, but it gives a warning that non-serializable values found.
React navigation warning
Also, I have heard that passing setState function to child component is very bad practice as it is mentioned here
I also tried navigation_events along with useEffect , this was a surprise to me that it didn't work either.
When I refresh the screen manually, or logout and log in, then it refreshes completely, but doesn't when I go back from application.
React.useEffect(() => {
navigation.addListener('focus', e => {
fetchConvos();
});
return () => {};
}, []); //also tried [navigation] instead of []
const fetchConvos = () => {
fetch('http://localhost:15000/' + id + '/conversations', {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
redirect: 'follow',
referrerPolicy: 'no-referrer',
})
.then(res => res.json())
.then(received => {
if (received?.response !== false) {
setConversations(received);
}
});
};
I have checked the id , received and even setConversations, they all are updating, but my screen still rendering the old messages.
Also, I don't want to use Async Storage or redux for this simple problem like this.
I can share the complete code if this isn't enough.
EDIT
I figured out one more way to update it may help clarify the situation more.
React.useEffect(() => {
navigation.addListener('focus', e => {
setConversations([]); //first setting convos to empty
fetchConvos(); //then fetching new data
});
return () => {};
}, []);
But this method is quite slow as I am updating the state of conversations twice.
I would appreciate if someone can help me here.
By taking the last 2 samples of code, I'd go down the route of setting the state to change the data. Like, I don't know the structure of your code completely. But i'm assuming you're using useEffect inside some component, right? In that case, React Context might be what you're looking for:
How to use react hooks on react-native with react-navigation
It allows to share informations, without having to build a structured store like redux. You should probably working on redesign a bit the code as, if you're following the current logic, you're going to split data pool of the conversation in the menu and load them when the "back" navigation event occurs, right?
Whilst the conversation data should be shared and available to both components, regardless where you're.
At least I'd rethink it this way to allow consistent data throughout the whole application.
Unless you've to do something specific and on-spot, of course.

Fetch status 200 but pending endllessly, except first call

I've been searching to solve this problem for a while but couldn't find a working solution.
I'm making a simple social network website and this API returns a article data such as text, image and video url, etc, all saved in server's local MySQL Database. My front-end is React and server is Nginx reverse proxy with Node.js using Express. When I load the page, I create 5 React components that each make fetch request for given article number.
The following code snippet is the fetch API that asks the server to fetch data from database:
//server-side script
app.get('/api/getArticle/:id', (req, res) => {
const con = mysql.createConnection({
host: 'myhost_name',
user: 'myUser',
password: 'myPassword',
database: 'myDB',
});
con.connect(function (err) {
if (err) {
throw err;
}
console.log("Connected!");
})
const idInterest = req.params.id.toString();
console.log(idInterest)
let sql = 'some_sql';
con.query(sql, function (err, result) {
if (err) {
res.status(500).send("Error while getting article data");
return;
}
else {
res.set('Connection', 'close')
res.status(200).send(result);
console.log("ended")
con.end();
return;
}
})
}
//React script
//index.js
fetch('http://mywebsite.com/api/getMaxArticleId/')//Retrieve top 5 article ID
.then((response) => {
for (let i = 0; i < data.length; i++) {
nodesList.push(<Container articleId={data[i]['id']}/>)
}
ReactDOM.render(<React.StrictMode><NavBar />{nodesList}<Writer writer="tempWriter" /></React.StrictMode>, document.getElementById('root'));
})
//Container.jsx; componentDidMount
const url = "http://mywebsite.com/api/getArticle/" + this.props.articleId.toString();
fetch(url, {
method: 'GET',
credentials: "include",
}).then((response) => {
response.json().then((json) => {
console.log(json);
//processing json data
This used to work very fine, but suddenly the getArticle/:id calls started to show 200 status but 'pending' in 'time' column in Chrome network tab, endlessly, all except the first*getArticle/:idcall. This prevents my subsequent .then() in each Container from being called and thus my entire tab is frozen.
Link to image of network tab
As you see from the image, all pending fetches are missing 'Content Download' and stuck in 'Waiting(TTFB)', except the first call, which was '39'
I checked the API is working fine, both on Postman and Chrome, the server sends result from DB query as expected, and first call's Json response is intact. I also see that console.log(response.json()) in React front-end shows Promise{<pending>} with *[[PromiseStatus]]: "Resolved"* and *[[PromiseValue]]* of Array(1) which has expected json data inside.
See Image
This became problematic after I added YouTube upload functionality with Google Cloud Platform API into my server-side script, so that looks little suspicious, but I have no certain clue. I'm also guessing maybe this could be problem of my React code, probably index.js, but I have no idea which specific part got me so wrong.
I've been working on this for a few days, and maybe I need common intelligence to solve this (or I made a silly mistake XD). So, any advices are welcomed :)

How to get rid of this - message:{'You are not subscribed to this API.'}.?

import React from 'react';
import './App.css';
function App() {
fetch("https://spoonacular-recipe-food-nutrition-v1.p.rapidapi.com/recipes/quickAnswer?q=How%20much%20vitamin%20c%20is%20in%202%20apples%253F", {
"method": "GET",
"headers": {
"x-rapidapi-host": "[host]",
"x-rapidapi-key": "[key]"
}
})
.then(response => {
console.log(response);
})
.catch(err => {
console.log(err);
});
return ( <
div className = "App" >
<
form className = 'searchform' >
<
input className = 'search-bar'
type = "text" / >
<
button className = 'search-bar'
type = 'text' > Submit < /button> < /
form > <
/div >
);
}
export default App;
if you have everything set up correct on RapidAPI I would suggest passing the key without using variables because maybe you are passing undefined. For undefined we get exactly the same error.
You have to select a pricing plan, even if it's the free plan. Go to "Pricing" on the page for the API, and select a plan. This fixed the issue for me.
https://api.rakuten.net/Top-Rated/api/e-mail-check-invalid-or-disposable-domain/discussions/9487/keep-getting-this-response-%22You-are-not-subscribed-to-this-API.%22
Be sure that you actually subscribe tothe api on rapid api hub if you aren't subscribed to the api
you're going to get an error message that notifies you don't have an active subscription and your call will not
be successfulbe sure that you actually subscribe tothe api on rapid api hub if you aren't subscribed to the api
you're going to get an error message that notifies you don't have an active subscription and your call will not
be successful
message: you are not subscribed to this api rapidapi
video example
Make sure you select a plan whether it's a free plan or a premium plan.
https://rapidapi.com/tipsters/api/hotels-com-provider/pricing
If you are subscribed to the free plan and still getting this error "message:{'You are not subscribed to this API.'}"
The problem might be related to the .env location. Try placing it inside root of your folder, rather than src folder.
Maybe the problem is with GET, try this. And let me know, if it works or not.
state = {
url:
"<url>",
header: {
"x-rapidapi-host": "<host>",
"x-rapidapi-key": "<key>"
}
};
componentDidMount() {
axios
.post(this.state.url, this.state.header)
.then(res => {
console.log(res.data);
})
.catch(err => console.log("err" + err));
}
For anyone else encountering this issue: the oversight I made was adding 'Bearer ' in from of the RapidAPI key. It should just be the key, without anything in front of it.
This may be stale, but helpful to someone else:
The first thing you want to do is to avoid placing it within .env.
Ensure it works properly within the code snippet (unsafe method) before placing it within .env (current best practice).
For instance, you should first do this:
const options = {
'X-RapidAPI-Key': 'theVeryLenthyKey',
'X-RapidAPI-Host': 'theAPI.p.rapidapi.com'
}
};
Instead of this:
const options = {
'X-RapidAPI-Key': process.env.THE_VARIABLE_NAME_YOU_USED,
'X-RapidAPI-Host': 'theAPI.p.rapidapi.com'
}
};
I got the same response saying {message: you are not subscribed ... but I was, I made sure. So the problem was that it couldn't read from the .env file.
So I used the manual method of directly putting the key in the code while in the development phase, but when I deployed the project I added it in the environment variables of the hosting service and it was working just fine.
If you are passing your key as variables, make sure you place your .env file in root folder (environment variables)
I have fixed this issue by simple directly passing key, like this
export const exerciseOptions = {
method: "GET",
headers: {
"X-RapidAPI-Host": "exercisedb.p.rapidapi.com",
"X-RapidAPI-Key": "xxxxxxxxxxxxxxxxxx",
},
};
don't pass the key through the .env file

Redux action on .then promise of another very slow

I have a redux action set up that posts to an external API, this updates a database, and returns the updated results. I then run another function inside to check a database table for new results:
this.props.updateAddTest(payload)
.then((response) => {
if (response.error) {
} else {
let payloadTwo = {
parentTestId: this.state.parentTestId,
bespokeTestId: response.response.testId,
selectedTests: selectedTests,
}
page.props.loadAvailableTests(payloadTwo)
.then((response) => {
page.setState({checkInvalidTests: response.response})
})
}
})
Running this code makes the network response time around 10 seconds - why does it take so long? Running the functions separately, it takes around 200ms. e.g just running:
this.props.updateAddTest(payload);
Why does nesting one redux action inside another slow it down so much?

Resources