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

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.

Related

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

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

Update React Component With Updated Data From Firestore

I have a chrome extension that stores data in Firestore and populates that data to the frontend. I always have to refresh the page to see newly added data, which isn’t a user friendly experience. How can I update the UI to show the newly updated data without having to refresh the page?
So far, I've tried using useEffect to get the data. Inside of it, I'm using a function that gets data from Firestore cached inside of chrome local storage.
Here is my code
const getFolderData = () => {
getDataFromChrome("docId").then((res: any) => {
setDocId(res.docId);
});
getDataFromChrome("content").then((res: any) => {
//console.log("getting in mainfolder",res);
// for (const item of res.content) {
// if (item.type.toLowerCase() === "subfolder") {
// // console.log(item)
// getSubFolder(item.id);
// }
// }
for (const item of res.content) {
setTiersContent((pre: any) => [...pre, item]);
}
});
};
useEffect(() => {
getFolderData();
}, []);
I also get this error. I'm also using the chrome extension API to communicate with a background script. It could be related to the problem
Uncaught (in promise) Error: A listener indicated an asynchronous response by returning true, but the message channel closed before a response was received
I've never used firebase so I'm not sure what your functions do, I can only guess. A few things wrong from what I can see:
Your useEffect is set to only run on page load since the dep array is empty, I assume you want to refetch on some condition.
If any of the 2 functions is supposed to be a subscription, your useEffect needs to return a cancel function.
Refetch data when needed is not a new problem, packages like React Query has tools that optimize your requests and refetch when needed. I suggest you give it a shot if your app has more than 2-3 fetch requests.

React-Native unable to store state getting null

I'm new to React-native so if there is a misunderstanding please be super clear and treat me as if I have never seen React-native before.
I have the app so that when you press on a button it will send you into an Auth0 flow where you can log in to the app. This seems working. If I log out the access token directly in the callback I am successful in getting it at the credentials.accessToken variable/location. However, when I am trying to set the state of the accessToken variable I get back null when I try to log it out to the screen via an alert or even via console.log. What am I doing wrong to cause this? I tried searching SO and google but both seem to show this as the right way of doing it.
Code:
const [accessToken, setAccessToken] = useState(null)
const onLogin = () => {
auth0.webAuth
.authorize({
scope: 'openid profile email'
})
.then(credentials => {
setAccessToken(credentials.accessToken)
Alert.alert('Access token: ', accessToken)
})
.catch(error => console.log(error)) // Eventually send this to backend for crash reporting
}
This is probably a case of a state not updating immediately. Try to use a useRef() instead of a useState(https://www.w3schools.com/react/react_useref.asp). If the problem is solved the issue was with the fact that states are updated asynchronously and hence it was not set to its most recent value (the value you expected) when you console logged it.

keep the data in app even when i navigate to other pages and back

I'm using React and Next.js with Firestore. On one page I get data from Firebase with useEffect only once the page is rendered. But since the get is kind of costly (lots of read), I want to persist the data fetched even when the user navigates to other pages and back to this page, so that I don't need to fetch again. How can I do that? Thanks!
useEffect(() => {
var newObj = {};
FB.getAllFiles()
.then((snapshot) => {
snapshot.forEach((doc) => {
const data = doc.data();
newObj[data["name"]] = data["count"];
});
})
.then(() => {
setCounts(newObj);
})
.catch((e) => console.log(e));
}, []);
There are multiple ways but one good way would be to use Context, create a data store context which would store your data and then you can read from it as a single source of truth.
As explaied in the other answers use the context and to make it even better you can persist the realtime listeners by controlling when you want to start or stop theme. Here is an example of Providers made for Firebase that do exactly the same. They also enable offline working for PWAs and persist state between reloads.
It's importand to understand that if you don't persist the realtime listeners you won't have any benefit from storing the data into any local state. Calling the listener again would load all data again and cost you the same. The only benefit from that would be that the user has the local data initial so it looks like the app is faster.

React state updating on its own after axios request?

I'm currently working on a React app where I'm using React's context API to put together the business logic of the app.
Inside the context all CRUD functions make axios requests to the API and return promises that can be used by components to deal with error handling and input. This is the context https://github.com/luckyrose89/notes-app/blob/master/src/AppContext.js
My app has a notebooks array that will contain individual notebook objects. And each of these objects has a notes array that refers to the notes made in each notebook. This is the APIs controller https://github.com/luckyrose89/notebook-app-backend/blob/master/controllers/notebook.js
As long as I was creating, reading, updating and deleting the notebooks, I had to make changes to the state in app context for things to get updated in my app. But when I create, read, update or delete anything in the notes array of the notebooks, I do not have to make those changes to the state. I return the promise and the state changes on its own. Can anyone help me understand why is this happening?
For example when I create a new note in a notebook and submit it using this:
handleSubmit = event => {
event.preventDefault();
this.props
.createNotepage(this.props.match.params.id, this.state)
.then(response => {
this.clearInputs();
this.props.history.push("/viewbooks");
})
.catch(err => {
this.setState({
errorMessage: err.response.data.error
});
});};
I don't have to change the state in the app's context by manually adding the response from the api to the notebook's notes array. Can anyone please explain this.
instead of passing this.state, try passing {...this.state} and see what happens

Resources