React JS: How should I manage a data refresh when compement updates? - javascript

I am new to React, and I wanted to re-request data to a REST server after login in case of "session expired error".
When the component mounts, it fetches the data from the network. However, if session has expired, I receive an unauthorized error. This error is managed by a parent component, which shows the login form to reactivate session. I did that like this because I have many components that should behave like that (re-request after login).
<Parent>
<Component1/>
<Component2/>
...
</Parent>
If a "session expired error" is triggered Parent's state changes and it shows the login form. After login, Parent's state changes again and all visible components should update their data. The ComponentX method called when Parent's state is changed is ComponentWillUpdate and ComponentDidUpdate, so I placed the following code in ComponentWillUpdate:
if (!this.state.gotData && !this.updating){
this.updating = true;
this.setState({gotData:true},
()=>{this.updating=false; this.getData();});
}
which does the following: if it was not able to get the data from the network and it is not getting data at the moment (!this.updating flag), it requests the data through this.getData method. gotData is updated in optimistic assumption, and it is set to false if getData method fails.
I know that setState should not be used in componentWillUpdate method, but I have not found a better solution to this. Maybe I could add a validSession prop to all components, then set it to false when "Parent" gets the "expiration" error, and set it to true when login is done. And re-request data from in componentWillReceiveProps instead of componentWillUpdate.
Another possible solution would be to subscribe components to a global "re-login" event, and do the data request inside the listener.
How would you do that? Maybe my approach is completely wrong from the beginning...

Related

Is there a function in ReactJS to wait for a Redux Action to be complete before loading anything in a class?

Is it possible to wait for a Redux action to be completed before loading anything in the class?
I have a page which uses data from logged-in users and the data is stored in a Redux state. In my React class, I'm calling the data in the Redux state to be used dynamically. If the user isn't logged in, the code will run into an error because the logged-in data is not present. How do I make it such that the code will wait for the Redux action to check if the user is logged-in, then if they are not logged in they will be redirected to another page before the data gets rendered and before the error occurs?
Anything like
on('REDUX_ACTION_LOGGED_IN', () => {})
will be useful.
I also saw an online package about something like redux-wait-for-action. Can someone teach me how to implement this here? The documentation isn't very clear.
I think you could look at this a different way.
You could check if the variable/data exists and if not then do something else.
Redux re-renders the component when the state changes. This means you do not have to wait for it to happen.
Something like this in your render probably would do:
render(){
<div>
{someDataAfter.login ? <div> I am logged in!</div> : <div> I am logged out</div>}
</div>
}
Just for completeness sake here is a link to explain the ? (ternary if) in the render function.
Another solution is to wrap the component with another one. This parent component can check if the user is loggedin. If it is then you can display the logged in component so:
render(){
{loggedIn && <MySecuredComponent/>
}
This will not render or load MySecuredComponent until loggedIn is true. Meaning componentDidMount will not fire in MySecuredComponent until loggedIn is true.

Component not updated when state updated with same values with redux

I have a login flow in my react native app. When the user enters its credentials, an action is dispatch that verifies them and then dispatch an other action in order to modify the state by the reducer.
In my component, I register to changes this way:
function mapStateToProps(state) {
return { userConnected: state.user.connected }
}
export default connect(mapStateToProps)(LoginScreen)
And I perform action when new props are received with that function:
async componentWillReceiveProps(newProps){
if(newProps.userConnected){
this.props.navigation.navigate("Home");
}else{
this.showWrongCredentials();
}
}
The first time when the user enters wrong credentials, the props are updated with connected = false, so it shows the wrong credentials message. If the user clicks another time with some wrong credentials, the state received the same values connected = false, so the method componentWillReceiveProps is not called and I cannot show the wrong credentials message again.
How can I do that ? Everytime the state is updated, even if the values are the same, I would like the method componentWillReceiveProps to be fired.
Yes, such behaviour is by design.
From react-redux doc
If performance is a concern, the best way to improve performance is to skip unnecessary re-renders, so that components only re-render when their data has actually changed
Or if you like source
Note that selectorFactory is responsible for all caching/memoization of inbound and outbound props. Do not use connectAdvanced directly without memoizing results between calls to your selector, otherwise the Connect component will re-render on every state or props change.
You mentioned that user clicks when entered credentials. I suggest to change some property of store as a result of user click. So for every click credentials will be verified and message printed.
Introduce additional variable to store which will be used as flag for every user click after credential has been entered. It can be named credentialEnteredFlag and set to true every time user entered credentials. Then have action to reset credentialEnteredFlag to false from componentWillReceiveProps.
componentWillReceiveProps will look like
async componentWillReceiveProps(newProps){
if(newProps.credentialEnteredFlag){
this.props.resetCredentialEnteredFlag();
if(newProps.userConnected){
this.props.navigation.navigate("Home");
}else{
this.showWrongCredentials();
}
}
}

React - Passing state to parent component - too many AXIOS requests

I'm a beginner both in programming and React and I have to create a functioning google Map single page website. I'm using google-map-react.
I have a parent App.js (containing the call to and a HTML sidebar) and a child Map.js containing the map itself and axios request function.
I'm making axios requests to fetch data from foursquare api. It works without side effects. Then I want to pass those data to my app.js and update the parent state so that I can renderthe locations on the sidebar.
This is the function I used (in Map.js). I had to put the call in componentWillReceiveProps as a last resource because componentDidMount didn't work:
https://jsfiddle.net/kd1yuhe5/
I think this may be the issue, but it's also the only way I found to make the list show:
this.props.updateVenues(this.state.venues)
This is the code from App.js
updateVenues(venues) {
this.setState({
venues: venues,
});
}
Then I called the method like this:
<Map updateVenues={this.updateVenues.bind(this)} />
The code works, venues are shown in the sidebar (if you need the code let me know, but I don't think it's relevant), but the I keep making requests until I exceed quota.
Again: I'm a beginner. I just started 3 months ago.
EDIT:
Here are both components:
Map.js
https://jsfiddle.net/kd1yuhe5/5/
App.js
https://jsfiddle.net/xwzrm4bp/2/
When the state of a React component is updated (and without custom implementation of componentShouldUpdate), it triggers a re render of that component (ie call the render function).
If the props of the children of this component have changed since the last render, they will also re render.
They re render because they have received new props, and this will also call their componentWillReceiveProps function.
Since you are fetching data each time Map will receive props, you are fetching data each time something change (state change) on App.
First in Map.js, this.props.query is assigned to this.state.query.
This looks like an error, as in this case what you want are the new props receceived by componentWillReceiveProps, this is the first argument of this function.
So you should assign props.query to this.state.query instead.
Except that actually you should not:
this.state.query is only used in componentWillReceiveProps, therefore there is no need to put props.query into state.query.
Second since you have both this.props.query from the previous props update and props.query which is the new received query, you have the opportunity to fetch only when the query has actually changed:
// Receive the update query from parent and fetch the data
componentWillReceiveProps(nextProps){
if (this.props.query !== nextProps.query) {
this.fetchData(nextProps.query);
}
}
Now you may ask, "ok but why my Map component was always re rendered, even when its props didn't changed".
But they did:
in App.js
<Map
query={this.state.query}
center={this.state.center}
updateVenues={this.updateVenues.bind(this)}
getClickedMarker={this.getClickedMarker.bind(this)}
/>
By calling this.updateVenues.bind(this) and this.getClickedMarker.bind(this) in the render method, you are creating new values (actually new Function references)for the updateVenues and getClickedMarker props, at each render.
Instead, you should bind these method in the contructor of App:
constructor(props) {
super(props);
this.updateVenues = this.updateVenues.bind(this);
this.getClickedMarker = this.getClickedMarker.bind(this);
....
}
....
<Map
query={this.state.query}
center={this.state.center}
updateVenues={this.updateVenues}
getClickedMarker={this.getClickedMarker}
/>
This may limit your API calls a lot, you may also debounce them.

How to watch for state/UI changes in vue.js

is there a way I can watch for a state change in the UI?
For example, in my data component I have a variable called loggedIn. If the value of loggedIn is equal to false, I want my header to display a Login link. Likewise, if the value of loggedIn is equal to true, I want my header to display a Logout link. So far I have tried:
<li v-if="loggedIn"><a v-link="{ path: 'login' }" v-on:click="logout()">Logout</a></li>
<li v-else><a v-link="{ path: 'login' }">Login</a></li>
data: function () {
return {
loggedIn: this.isLoggedIn() //this method returns true/false
}
},
The code as is has the desired effect. However, it only works if my component is refreshed. I would like Login/Logout to render accordingly when the value of loggedIn changes... Can someone help?
Thanks in advance!
Update I just realized that this.isLoggedIn is a method in your component, and not outside it. It looks like you should make isLoggedIn a computed and use that in your directives.
Yes, you can watch for a state change in the UI, but Vue cannot. Vue does not know that the UI as a whole exists. It only knows about the pieces you tell it about (usually via directives).
In your example code, you initialize loggedIn to the output of a function, but after being initialized, its value is never changed. There is no $watch for things that are outside the ViewModel.
Presumably, there is some login process in which the UI state changes. Since the purpose of the ViewModel is to model the application, you should implement that
login process as a method in your ViewModel. Among the things it will do is change the value of the loggedIn variable. As a general rule, UI state should be represented by data members, and anything that changes UI state should be implemented as a method.
It may be that logging in happens up the parent tree from this component. In that case, loggedIn should come in as a prop.

Passing Huge data between Vue js routes

I have an application which searches for flights using Vue.js and Vue Router.
I have two components, first one is search, which is on the base route '/'. When user clicks on search, it will send a request to server and gets a huge list of flights.
Then I need to call the result component on '/results' route and show the results using v-for.
I have two questions, first, how can I manually redirect to '/results' after I get the results.
Second and more important, what is the proper way of passing the results data to results component to use?
Inside your results components, you can put transition hooks in the route object. Read here: http://vuejs.github.io/vue-router/en/pipeline/hooks.html
The activate hook runs when a component is activated, and the component wont appear until the activate hook has run. Here's the example on that page, which would be similar to yours:
route:{
activate: function (transition) {
return messageService
.fetch(transition.to.params.messageId)
.then((message) => {
// set the data once it arrives.
// the component will not display until this
// is done.
this.message = message
})
}
}
So basically when they click search you send them to /results and this hook will handle loading the data in between.
Here's an advanced example of a mail app using vue-router that shows off a lot of the transition hooks in action: https://github.com/vuejs/vue-router/tree/dev/example/advanced

Categories