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
Related
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)
}
}
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.
I am using React and Next.js and trying to redirect a user from a page when the data for that page is not available using Router.push('/another-page').
To do this I am checking for a status code in getInitalProps and applying a conditional. It looks like this:
const statusCode = action.currentArticle ? 200 : 404
if (isServer) res.statusCode = statusCode
if (statusCode === 404) {
Router.push('/')
}
The status code is being set properly and it makes it inside the conditional, at which point I am greeted with this error: No router instance found. You should only use "next/router" inside the client side of your app.
Actually, I am getting the same error no matter WHERE in the component's lifecycle events I try to redirect, and am getting little info online about this error.
The pattern of redirecting from getInitalProps can be seen in this next.js wiki: HERE
Any ideas on why this error is occurring or how to fix it are much appreciated ;)
With Next.js (and any universal react rendering) your code is executing in two different environments. First in Node (on the server) and then in a browser. Next does some work to provide unified functions that run in both these environments but they're very different. Next can't and doesn't keep this from you. It seems like you just loaded a page in your browser but here's a little more detail on what's really going on…
On the client/browser:
Type url in the address bar (localhost:3000 or whatever), press enter.
GET request goes out to the server (Node).
On the server/Node:
GET request comes in.
Node gives you a request and a response object.
Maybe you have some Express routing/middleware.
At some point Next's render() function is called with the request and response objects.
Next runs getInitialProps and passes in the request/response.
React renderToString() is called which calls the following React lifecycle methods:
constructor()
componentWillMount()
render()
React creates a string of HTML that gets sent to the client.
^ This is Node. You can't access window, you don't have fetch, and you can't use the Next Router. Those are browser things.
Back on the client:
HTML is downloaded and rendering begins.
Links to js/css files in the HTML are downloaded/run.
This includes js code compiled by Next.
React render() is run which associates the downloaded HTML (the DOM) with a React virtual DOM. The following React lifecycle methods will run:
constructor()
componentWillMount()
render()
componentDidMount()
All other lifecycle methods (updates) will run when props/state change.
^ This is the browser. You have window, you have fetch, you can use the Next Router. Now you don't have the Node request/response but that seems to catch people up less.
Ref: Component lifecycle
The way works like #Shi said, but there is not server in getInitialProps. Instead of that, there should check window:
getInitialProps({res}){
if(typeof window === 'undefined')
res.redirect('/');
else
Router.push('/');
}
You can redirect from getInitialProps() like this:
import Router from 'next/router'
static getInitialProps = (ctx) => {
// On server
if(typeof window === 'undefined'){
res.writeHead(302, {location: '/dashboard'})
res.end()
} else {
// On client
Router.push('/dashboard')
}
return {}
}
See https://github.com/zeit/next.js/issues/649
next/router is not available on the server that's way you get an error saying that router not found, next/router can only be used on the client side.
For you to redirect a user inside getInitialProps in the server you can use:
getInitialProps({server,res}){
if(server)
res.redirect('/');
else
Router.push('/');
}
To make sure the page never render, we need to add await new Promise(() => {}) to end. The promise no needed resolve anything.
Home.getInitialProps = async ({res}) => {
if(res) {
res.writeHead(302, {location: '/dashboard'});
res.end();
} else {
// window.location.href = '/dashboard';
// Or with SPA redirect
Router.push('/dashboard');
}
await new Promise(() => {});
return {}
}
I found this https://www.npmjs.com/package/nextjs-redirect to be very simple and solved the issue for both client and server side.
pages/donate.js
import redirect from 'nextjs-redirect'
export default redirect('https://paypal.me')
I am searching solution for this question more than 3 day and can't find anything..
I have ionic3 App and working width Http POST requests. I am sending requests to my php server and geting data..
My data-api.ts (provider)
public getNotifications(token){
return this.http.post(this.sharedVars.getApi(),"action=messages/notification&token="+token, this.options
).map(res => res.json());
}
profilePage.ts
notifications() {
this.api.getNotifications(this.user.token).subscribe(
data => {
if(data.err == 0){
this.notifications = data.data;
}
},
err => {
console.log(err);
}
);
}
This is working functions and I am getting right output (1) when click this function. but on x action on my server notification count will changed to 2, 3, 4 etc.. and I want load this function not on click, but on page load. so If this.notifications have new value I want change value live (like as firebase)
Example 2:
I have send message action in my data-api.ts (provider)
public sendMessage(token, to, message, attachment){
return this.http.post(this.sharedVars.getApi(),"action=messages/send&token="+token+"&to="+to+"&message="+message+"&attachment="+attachment, this.options
).map(res => res.json());
}
and also have function to get this messages.
public getActivity(token){
return this.http.post(this.sharedVars.getApi(),"action=messages/getActivity&token="+token, this.options
).map(res => res.json());
}
so if I am making post request to sendMessage then I want listen live getActivity action and load new message in my page but not reload.. like as firebase..
I hope this question is clear. because I am not english speaker and tryng to find solution. Tanks
Listening actively to live events is not possible with a single HTTP request in angular.
However you might look into eventSources.
Look at this question for using with angular 2+ :
Creating an RxJS Observable from a (server sent) EventSource
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.