Why componentDidUpdate is executing repeatedly - reactjs

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)
}
}

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.

Redux state is updated but UI is not updating

I have created simple app to add user using redux and antd library. When user is added I am trying to show success message. I am able to get that updated message in state but when I alert that message it shows blank. When I again click the button then it shows success message in alert.
I have created codesandbox link : https://codesandbox.io/s/admiring-waterfall-v04g9
Please help me out, I am quite new to react.
here is what possibly happened
- since your callback function is synchronous, eventhough the addUser function is placed before alert function, it is not a guarenty that it will execute before it.
solution is to make your function asynchronous as follows to guaranty that the user is added and hence state updated (with the user and the message you are displaying) before the alert is fired. make sure to save changes and refresh the sandbox to test it.
handleSubmit = e => {
e.preventDefault();
this.props.form.validateFields(async (err, values) => {
if (!err) {
await this.props.addUser(values);
alert(this.props.message);
}
});

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

Redirecting from getInitalProps in React/Next.js

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')

Flux architecture for a login or basically most forms processing

I am trying to understand a bit more about flux architecture and am designing a simple login component. Suppose when you login (POST ajax) and an error comes back. How would the information flow in flux?
I think the LoginComponent should do the work on a handleSubmit function.
After the ajax call comes back with an error and message should the component create an action such as "UpdateLoginStatus with payload {message: "no e-mail found"}. This would then trigger a LoginStore or something to save the status message and then it would emit an event such as "LoginStatusMessageChanged".
Another totally different component called LoginStatusMessage would register to listen to events on the LoginStore. It would get notified of this event and then proceede to update it's own state with the message. It would go out to LoginStore and fetch the message and display it to the user via render.
I believe this example covere your question quite well: https://github.com/przeor/react-router-flux-starter-kit/
Don't let your Component do the request. That would be mixing UI with business logic. That is always bad.
Entered username, password and button should be handled by your React component. Whenever the button gets clicked, the component should trigger an action.
//Component.js
handleSubmit: function(){
UserActions.login(this.state.username, this.state.password);
}
The action informs the store:
//UserActions.js
login(username, password) {
this.dispatch({username: username, password: password});
}
The Store then executes the needed AJAX (as the actions never should cause changes or requests themselves).
Depending on success or error, your store then triggers a new action. It should never save the response directly. Never save data in stores without an preceding action. If you want to, you can save that you are currently logging in (For instance, if you want to animate a spinner)
//UserStore.js
handleLogin(credentials) {
this.isLoggingIn = true;
AuthService.login(credentials.username, credentials.password).then((user) => {
UserActions.loginSuccess(user);
}).catch((errorMessage) => {
UserActions.loginFailed(errorMessage);
})
}
The actions again do their dumb thing, as they always should be nothing but dumb messengers.
//UserActions.js
loginSuccess(user) {
this.dispatch(user);
}
loginFailed(errorMessage) {
this.dispatch(errorMessage);
}
Up next: handle the actions in your store!
//UserStore.js
handleLoginSuccess(user) {
this.user = user;
this.isLoggedIn = true;
this.isLoggingIn = false;
}
handleLoginFailed(errorMessage) {
this.errorMessage = errorMessage;
this.isLoggingIn = false;
}
That's it. As long as your component listens to the UserStore, it get's informed when your user logs in, is successfully logged in or had an error, while having a clear separation of Logic and UI and a unimanual flow.
EDIT: Code examples are mostly pseudocodish. Actual implementation depend on your Flux framework.

Resources