So, I have an app that user can choose several settings in. Like number format or a currency that will be displayed by default.
For that there is a special API endpoint /settings that I get the values from.
The whole app is rendered on the server side when the user reloads the page. I fire the settings fetch as soon as possible (in the componentWillMount of the top level component), but there is sometimes a blink/ delay.
Possible solutions:
First solution is to fetch the settings when the user loads the app for the first time, and save it in the localStorage if it's available. Downside is that the numbers/ currencies still can be different than those in the settings when the app is loaded for the first time.
Second solution would be to fetch the data before the application is rendered on the server side, and inject this data somewhere into script tag (like a window.userSettings = { ...settings }). It might extend the reload loading time a bit, but the settings will be as the user set them.
Is there any other solution to such a problem? What is the best way to do it?
I hope, this solution may help you.
step 1: Perform API call in componentWillMount and also, check for error. Assign two state one for currencies and one for error, if possible another one for loading
Step 2: Assign localStorage using your state in componnetDidMount
Step 3: Then, under your render method, check for both error state and localStorage
Sample snippet in given below
class App extends Component{
custructor(props){
super(props);
this.state={
currency_number: '',
error: false
}
}
componentWillMount(){
//perform fetch api and assign the state with the received items
// check for error, if so then setState({error: true})
}
ComponentDidMount(){
localStorage.setItem('currency', JSON.stringify(this.state.currency_number))
}
render(){
const currency = JSON.parse(localStorage.getItem('currency'))
if (this.state.error) {
return(
<div>
{ currency }
</div>
)
}
}
}
Related
It should be noted that I am using Redux with React-Native.
For simplicity sake, lets say I have an email viewer. There's a homepage with a massive list of emails and when you tap on an email, you are navigated to another screen. Here, the contents of the email, title, subject, body, is all held in a piece of state managed by redux. This all works fine.
Lets say I back out of that page, and now navigate to another email. The previous email pops up for a split second before the new email is shown. It should be noted here that I am storing the data in AsyncStorage as a sort of "cache?". The issue here is that since I only re-update the state whenever I tap on an email, the state which is the body of the email viewing page gets updated a split second after the user is navigated to it. This, is annoying.
The heart of the question is this
How can I store the body of my data in another piece of state, functionally identical to the current-email-viewing-state without overwriting the currently active state?
or
is this even the best way to do this?
Thanks
You could use Redux's lifecycle methods to handle this. Let's say the state for your email detail component looks something like this:
export const initialState: StateShape = {
loading: false,
readOnlyEmailData: {
recipientEmails: null,
senderEmail: null,
body: null,
},
};
When the email detail component (let's call it EmailDetail.jsx) is loading, you can use ComponentDidMount() to get and set your values.
You'll probably have actions and actionCreators like getEmail, getEmailSuccess, and getEmailError. Set loading to true in getEmail, and then false again on success or error. You can add a conditionally rendered spinner component (easy to borrow from something like MaterialUI) to EmailDetail, which is visible only when loading is true, and render the rest of your content when loading is false.
When the user hits the back button or otherwise navigates away from the component, componentWillUnmount() can be given a list of things to do as the component prepares to unmount. In this case you could use a reset method to reset loading and readOnlyEmailData to initial state on unmount.
When the user clicks on a new email, it will load the new email data on mount and show the spinner until the new email data is available.
There are other ways to do this, this is not the most optimized, but it should work quite a bit better than what you've tried so far. I hope it helps :)
I have dropdown filter to show items by date, for example and show data for last 24 hours, show data for last 3 days.
I have defaultState in my reducer
const defaultState = {
dataArray: [],
a: true,
b: false
}
By default dataArray is empty.
And I have reducer and action. In componentDidMount method I fetch data from server by dispatching some actions.
If I refresh page, default page that list last items for 24 hours is empty because dataArry comes from defaultState in my reducer. But if I change page to list data for last 3 days then componentWillReceiveProps works and inside this method I fetch data and it reduce my state and returns new one with
dataArray = [{some data}]
How to fetch data and set it to state to render it after page was refreshed?
Add to defaultState a 'loading' variable and initialize it to true. When fetching of data is completed, set it to false.
In your component check this variable. If it is true display a spinner and/or a loading message, otherwise display the data.
In addition to that, every time you start fetching, before fetching, fire an action called 'FETCH_START' which will set the loading variable to true.
If there is an error in fetching, you can set another state variable to the error message. This variable will be initialized (every time you start fetching) to an empty string. If loading is completed you can check this error variable, and display the error message if there was an error, instead of displaying the data.
This process is useful for various cases, such as authentication, etc.
Yossi's answer is pretty spot on. I just want to take it a step further and mention that you can also enhance your users' experience by knowning when to fetch data. For instance, if you're on a landing page and know most of your users are going to be browsing your store, start fetching that data on the landing page while nothing is happening.
To go along with that, take a look at this: Google Dev's requestIdleCallback()
I have a simple question about coding style for single page application. My front end is using React Redux
For example I have a standard CRUD page where data is displayed in table and pop up modal form. Data table is filtered from the server not from the client.
My question : If i create, update or remove a data should I call a refresh function or just edit it in redux store?
Refresh function :
Data always updated
Newly added data is filtered
Two times request, slower, unresponsive (Main problem)
Redux store:
App looks responsive
One time request
Lost server side filter function and data is not updated if multiple users is using the app (Main Problem)
Any advice will be appreciated
Edit the store locally to give immediate feedback, then send the request and when you get the reply back consolidate the store with the new data
basically, do both things and get the best benefit of both worlds
Dispatch an async action which queries the server where filter happens and when it resolves, update redux state with the refreshed, filtered data.
Pseudocode
// dispatches an action to refresh data without page reload
export function refreshDataAction() {
return (dispatch, getState) => {
return (
fetch('api/data', options) // fetch the data from server and let it filter
.then(data => dispatch(updateDataAction(data)))
);
};
}
// dispatches an action to update redux state with filtered data
export default function updateDataAction(data) {
return {
type: 'UPDATE_DATA',
...data,
}
}
Then you could just call dispatch(refreshDataAction()). Data is filtered, no page refresh.
Calling refresh in a React application (not only React, but any real-time front-end app) kind of defies whole principal of using React.
What you should do is, whenever there occurs a data-changing operation in your client, you should trigger an API call, that alters your server-side data accordingly. Send the data back to the client (you can send it to all clients, if you fancy web-socket), save it to the Redux state to trigger a re-render.
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
I implemented J-Toker in my frontend app using React and flow (with a Rails API backend), in a similar way to this project react-flux-jwt-authentication-sample
The dashboard should only be accessed by logged in user. In order to do this, J-Toker calls my Rails API and return the loggedIn User (or not if no one is loggedIn...) that is accessible using Auth.user.
So I have my Dashboard component wrapped in an AuthenticatedComponent:
Dashobard.jsx
export default AuthenticatedComponent(Dashobard);
Here is my AuthenticatedComponent.jsx:
export default (ComposedComponent) => {
return class AuthenticatedComponent extends React.Component {
static willTransitionTo(transition) {
if (!LoginStore.isLoggedIn()) {
transition.redirect('/login', {}, {'nextPath' : transition.path});
}
}
...
}
Finally here is my LoginStore.jsx code:
import Auth from 'j-toker';
class LoginStore extends BaseStore {
...
isLoggedIn() {
return Auth.user.signedIn;
}
}
Everything works great except when I manually refresh the page. In this case, the Auth.user.signedIn return undefined even if the user is logged in and is redirected to the login page. I found that the problem comes from the fact that Auth.user is not yet loaded when LoginStore.isLoggedIn() is called because in this case Auth.user returns an empty object and if I wait a little (tested with a basic setTimeout) it returns the loggedIn user.
I am new to React.JS (using it for just a week now) and I'm not sure how to deal with this. I read A LOT of articles and cannot understand yet how React.JS is meant to be used, espacially with third party plugins (I also have a lot of problems using animation plugins but it's another story...).
Could someone help me on this?
It would be much appreciated :)
This happens because when you pass the configuration to J-Toker, the library will check for a stored token. In case it finds one it will validate the token with you API. This is why calling configure, it will return you a jquery Deferred object, which will be resolved after the validation has been done or rejected if there is no stored token. If the validation passes in you API the user object will be returned and set in Auth.user.
By following your example project you can fix this by rendering the component after the Deferred has been resolved.
var promise = Auth.configure({
apiUrl: 'your-api-host'
});
promise.always(function() {
router.run(function (Handler) {
React.render(<Handler />, document.getElementById('content'));
});
});