How to force Firebase database call to be synchronous? - reactjs

I am using firebase with reactjs and i have an issue for rendering data fetched from firebase.
My reactjs component loads before data, so when my webpage is loaded, it remains blank for a short time before data are displayed.
But i don't want my page to load without data have been successfully loaded before. So , i've tried to manipulate state in the constructor but i think the issue come from firebase. Because firebase fetch data asynchronously even in constructor() and componentDidMount()
so render() is called even if firebase didn't finish to send data.
How can i totally prevent react to render without firebase didn't send the result ?
constructor(props){
super(props)
post = database.ref().child('posts').orderByKey().equalTo(id)
post.once('value').then(snap => {
// I get the snap here but i don't want my page to render before
// all data queried have been fetched
}).then(function(){
// I've tried to add a "then" callback but it doesn't work
// Render is still displayed too soon
})
}
render(){
let data = this.state.data
return(
// simple example
<span>{this.state.data}</span>
)
}
thanks.

You can fetch the data, and then once you have it, and only once you have it, do you ReactDOM.render.
It doesn't stop the page from loading and your css will apply, there just won't be any content in body.

Related

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.

Why componentDidUpdate is executing repeatedly

I am trying to display user notes on submit. On componentDidMount I am sending a GET request to the server to initially display data. When a user submits a new comment, on componentDidUpdate I'm checking prevState. If there is any difference found it should load new data from the server.
But inside componentDidUpdate continuous request is sending to the server.
What I have done so far
componentDidUpdate(prevProps, prevState){
if(prevState.status !== this.props.userDisplayNotes.status){
// This is GET request to display the data from the server
this.props.displayUserNotes(this.props.user_id)
}
}
// Here I'm displaying data on initial rendering with GET request
componentDidMount(){
this.props.displayUserNotes(this.props.user_id)
}
// This is form submit handler
handleSubmit = (e) => {
e.preventDefault();
this.props.saveUserNote(this.props.user_id, this.state.userNote)
}
Upon successful submission of comment I'm getting a response from server like {"status":"success"}
For state management I'm using Redux.
But inside componentDidUpdate it is sending continuous request to server causing application crashed. Can someone please explain me what I'm doing wrong here?
You can compare previous Props with new one it will fix your problem
componentDidUpdate(prevProps, prevState){
if(prevProps.userDisplayNotes.status !== this.props.userDisplayNotes.status){
this.props.displayUserNotes(this.props.user_id)
}
}

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

Symfony4 backend with frontend react. best and safest way to pass information?

I am learning React and want to create an application with Symfony4 as my backend and React frontend. I am stuck now when I need to pass some kind of data to the frontend from my backend. I don't really know what is the right way to do it? Following some tutorials I am doing it like this:
From the controller I send some data to the twig file:
/**
* #Route("/")
*/
public function homepage()
{
$date = new DateTime();
$curr_date = $date->format('Y-m-d H:i:s');
return $this->render('base.html.twig', [
'gameDate' => $curr_date
]);
}
In the twig file, I set it as a data-attribute
base.html.twig:
<div id="root" data-event-date="{{ gameDate }}">
Then I can get the variable as a dataset in my JavaScript
App.js:
const root = document.getElementById('root');
ReactDOM.render(<Homepage {...(root.dataset)}/>, root);
And render it from props.
Homepage.js:
class Homepage extends Component {
constructor(props) {
super(props)
this.state = {
prizePool: '',
gameDate: '',
numberOfPlayers: ''
}
}
onParticipateClick = (event) => {
this.setState({prizePool: Math.random()})
}
render()
{
return (
<div className="mt-c-10">
<GameInfoBox gameDate={this.props.eventDate}/>
</div>
)
}
}
This actually works, but I am concerned with showing all the information in data variables because anyone can see it. What if I want to pass user ID or something secret? There should be another way to do it right?
It depend on what you attemps, if you are working on big project, you can use API to serve backend data. Take a look here: https://www.modernjsforphpdevs.com/react-symfony-4-starter-repo/. There is a simple example.
But if you want something more use api-platform or FOSRestBundle.
"Best and safest" is a little ambiguous - do you need strict security, or safe as in code stability etc?
Instead of passing your data from controller to view (twig) and then into HTML elements or global, another way is this:
Controller loads the view file with your nav and other stuff
Controller loads React (however you do this, Webpack etc)
React calls another controller (i.e. fetch()). This controller is probably somewhere like src/Api/Controller/ as it wont render a view so keep it separate to the other controllers which do render a view
The API controller calls your DB or remote API (etc) and gets the data and sends it back as JsonResponse back to React.
React can then show the data, or an error message depending on the response status
The API controller in your MW can also handle errors and do some logging, so React just gets a 200 and the data or a 400 (or whatever) and it can show a nice message to the user as normal.

How to delay component rendering when response from the server is delayed

I am a newbie to react. I have a situation where in I am fetching some data from the server which I want to display in a page. I am using websockets for communication between the client and server. As the server makes a request to some third party API the response gets delayed. But my component gets rendered before the response comes. I have seen answers which talks about handling such situation in case of ajax request. But how do i handle it in the case of web sockets. My sample jsx page which I want to render after getting response from server is as follows
import React ,{PureComponent} from 'react';
export default class ScorePanel extends PureComponent{
constructor(props){
super(props);
var d = new Date();
this.currentDate = d.getFullYear()+"-"+d.getMonth()+ "-"+ d.getDate();
this.week ="1";
this.numQuarters =1;
}
getInitialState(){
return {
resultsObtianed: false
}
}
getScores(){
return this.props.scores ||[];
}
render() {
return <div className = 'scorePanel'>
if ( !this.state.response ) {
return <div>loging response</div>
}
{
// data to render after geting response from server
}
}
How do I let the client know that response from the server has been received and it's time to render a component. It would be better if I can show a loading page if the response gets delayed. So I would like to make use of getInitialState function as well. I am dispatching an action to the server on a button click in the navigation bar. Thanks for the help in advance
Assuming you have a websocket ws, and listening to "test" event.
in your oncomponentDidMount do
componentDidMount()
{
ws.on("test",(data)=>{
this.setState({response:data})
})
}
also I like to predefine state, so I'd add this.state={response:{}} in constructor

Resources