I have a sort of Dashboard in my application. In this dashboard I let the user put many widgets (each widget is a class component). Each widget renders different stuff, such as, charts, images, and text. When I display it, each widget make an axios call to retrieve data from backend. I need a way to be able to tell when all the requests have finished so I can get the HTML completely rendered (I'm going to export it using HiqPdf later).
I need each widget to be independent so I can use in other components. That's why each widget make its own axios call. Otherwise I think I could make many axios calls in a single component that is above my widgets and then I would pass all the data as props to each widget. However, no... the axios calls must stay inside each widget.
I've found many places talking about promises, but every example talks show how to do it in a single component.
The reason I'm working on it is because I have the need to export it using a library call HiqPdf. This library receives a HTML as string and exports to PDF. Therefore, I need to know when the dashboard has been completely loaded to let the application export it.
Think about an event-driven framework that persists the global state of your single page app and notify subscribers whenever there is a change in the state.
One of the famous frameworks is redux.
Another simple framework is mufa. These are some similar questions that leverages mufa:
https://stackoverflow.com/a/42124013/747579
Stop the communication between react components using mufa after a condition
For your case, it might be something like this:
const all_calls = [];
const {on, one, fire, unsub} = mufa;
axios.get('call1').then((data) => {
fire('call1_received', data);
})
axios.get('call2').then((data) => {
fire('call2_received', data);
});
one('call1_received', () => {
all_calls.push('call1_received');
if (all_calls.length === 2) {
alert('!!!! All calls have been received')
}
})
one('call2_received', () => {
all_calls.push('call2_received');
if (all_calls.length === 2) {
alert('!!!! All calls have been received')
}
})
Note, one will subscribe once only.. while on subscribe forever.
Related
I have a simple app which pulls products from an API and displays them on-page, like this:
I've added Vuex to the app so that the search results as well as the product search array doesn't disappear when the router moves the user to a specific product page.
The search itself consists of the following steps:
show loading spinner (update the store object)
dispatch an action to access the API
update the store object with products, spinner
decide if the product list is exhausted
hide loading spinner
You get the idea.
With all of the variables stored in Vuex, it stands to reason all of the business logic should belong there as well, but should it really?
I'm talking specifically about accessing store params such as productsExhausted (when there are no more products to display) or productPage (which increments every time the infinite scroller module is triggered) etc.
How much logic - and what kind - belongs in Vuex? How much does not?
I was under the impression that Vuex is used for storage only but since all of the data is located there, fetching it all back to the Vue app only to send it all back seems like an overly verbose way to address the problem.
Vuex allows you to share data !
For everything that concerns the state of the app its pretty straightforward.
All the data that can be used by multiple components should be added
to the store.
Now concerning the business logic, even though I find its not really clear in the official documentation, it should follow the same principle.
What I mean is that logic that can be used by multiple components should be stored in actions.
Moreover actions allows you to deal with async operations. Knowing this, your code that pulls the data should definitely be stored in vuex's actions.
What I think you should do is to put the request inside an action, then mutate the state of your variables and automatically your UI will reflect the changes.
Moreover, a good pattern to apply is to convert most of the logic to a state logic. For instance consider this demo of a jumping snowman. In here the click action results on updating a value from the store. Although the interesting part is that one component uses the watch functionnality to be notified when the store changes. This way we keep the logic inside the component but use the store as an event emitter.
var store = new Vuex.Store({
state: {
isJumping: 0
},
mutations: {
jump: function(state){
state.isJumping++;
}
}
})
Vue.component('snowman', {
template: '<div id="snowman" :class="color">⛄</div>',
computed: {
isJumping: function(){
return this.$store.state.isJumping;
}
},
watch: {
isJumping: function(){
var tl = new TimelineMax();
tl.set(this.$el,{'top':'100px'})
tl.to(this.$el, 0.2, {'top':'50px'});
tl.to(this.$el, 0.5, {'top':'100px', ease: Bounce.easeOut});
}
}
})
tl;dr I would like to know where to place context specific multi-step async callback logic in a redux architecture, and if I am on the right track with the example code I supply below. By "multi-step" and "context specific" I typically mean server calls initiated by some user action (onClicks, etc) where the logic might only be relevant for a given component (such as redirect to a given route when successful).
The redux docs has this to say on code with side effects:
In general, Redux suggests that code with side effects should be part of the action creation process. While that logic can be performed inside of a UI component, it generally makes sense to extract that logic into a reusable function so that the same logic can be called from multiple places—in other words, an action creator function.
While that seems fine, I am not totally sure whether it is "correct" to put calls to my routing component in there, as these action creators usually seem quite generic, and triggering routing to some other resource in the app is usually quite context dependant.
I also find it a bit weird to put these quite-different beasts, that trigger action creators asynchronously and dispatch the resulting actions, in the same files (foo-model/actions.js) as the "clean" sync action creators. Is this the right place? When reading tutorials on Redux it seems like they live side by side.
The example code is quite simple and basically describes these steps:
On a user click, call a function with some param
This function calls another async function (such as a network call)
When the async call completes, trigger a routing action to another page
Background: I want to gradually refactoring a Meteor project by moving all Meteor specific bits out of the React components, eventually substituting Meteor in the front and back for something else. As there are about 50KLOC I cannot do this in one go, so I am gradually working my way through one route at a time, hoping to end up with a standard React+Redux+ReduxRouter package. In the current code routing, data fetching, and rendering is somewhat intertwined in each component, and I am having some trouble finding out where to put multi-step async logic, such as the example below.
Details on the stack I am trying to wrangle my way out of:
FlowRouter for routing
Meteor/MiniMongo for data mutation and retrieval
React Komposer for Higher Order Components
old Meteor code in MyContainerComponent
// triggered as onClick={(e) => this.saveEncounter(e.target.value)}
// in render()
const saveEncounter = (encounter) => {
Meteor.call('createEncounter', encounter, handleSaveResult);
}
};
const handleSaveResult = (err, encounterId) => {
if (err) {
this.setState({errorMessages: err});
} else {
// route to another page
NavigationActions.goTo('encounter', {encounterId: this.props.encounter._id || encounterId});
}
}
new redux code - moved into actions.js
I am trying to keep the implementation straight forward (no additional deps) at this point to understand the foundations. "Simplification" using redux-thunk, redux-actions or redux-saga need to come later. Modeled after the example code in the Redux tutorial for Async Actions
export const saveEncounter = (encounter) => {
function handleSave(err, encounterId) {
if (err) {
dispatch(createEncounterFailure(err), encounter);
} else {
dispatch(createEncounterSuccess(encounterId));
}
}
dispatch(createEncounterRequest(encounter));
Meteor.call('createEncounter', encounter, handleSave);
}
// simple sync actions creators
export const CREATE_ENCOUNTER_REQUEST = 'CREATE_ENCOUNTER_REQUEST';
function createEncounterRequest(encounter) {
return {
type: CREATE_ENCOUNTER_REQUEST,
encounter
};
}
export const CREATE_ENCOUNTER_FAILURE = 'CREATE_ENCOUNTER_FAILURE';
function createEncounterFailure(error, encounter) {
return {
type: CREATE_ENCOUNTER_FAILURE,
error,
encounter
};
}
export const CREATE_ENCOUNTER_SUCCESS = 'CREATE_ENCOUNTER_SUCCESS';
function createEncounterSuccess(encounterId) {
return {
type: CREATE_ENCOUNTER_SUCCESS,
encounterId
};
}
As you noted in a comment, Dan Abramov discussed a lot of the ideas behind handling async work in Redux in his answer for how to dispatch an action with a timeout. He also wrote another excellent answer in why do we need middleware for async flow in Redux?.
You might want to read through some of the other articles in the Redux Side Effects category of my React/Redux links list to get a better idea of ways to handle async logic in Redux.
In general, it sounds like you may want to make use of either "sagas" or "observables" for managing some of your async logic and workflow. There's a huge variety of Redux middlewares for async behavior out there - I summarized the major categories and most popular libraries in my blog post The Tao of Redux, Part 2 - Practice and Philosophy. There's also some interesting thoughts on a very decoupled saga-based Redux architecture in a post called Redux Saga in Action.
I see your point, you would like to have a way to divide and categorize your actions, is that right? Actions that will execute sync code, async code, logger, etc.
Personally, I use some naming convention. If I have to dispatch an action that has to fetch some data, I call it REQUEST_DATA. If have to store some data arrived from the server to the ReduxStore, I call it STORE_DATA.
I don't have a specific pattern. I also have to point that I divide my codebase based on the feature, so the modules where I define my actions are pretty small and neat
In my experience with Redux, I haven't found any problems with putting async calls inside action creators. I think redux-thunk or some other middleware is very helpful, even for a simple setup.
The only thing I'd add is that I don't find your sample code very readable.
Personally I've come to like the ducks pattern, but also just keeping action types, action creators and reducers in separate files would work to improve clarity.
Hope this helps.
I am kind of confused here on where to place my global functions. In a lot of examples a main.js file points to an app component and this is placed somewhere within the html. This workflow would be fine for me If I were to simply contain all my logic within this app component. But I am combining components with Laravel functionality so this does not work for me.
Currently my main.js file contains a bunch of methods that I need to have access from anywhere in my app. These methods don't contain any broadcasting events so they can effectively be placed anywhere as long as they get a vue-resource instance.
My main.js file:
https://github.com/stephan-v/BeerQuest/blob/develop/resources/assets/js/main.js
Hopefully somebody can tell me where I could place my friendship methods if I were to use vuex or in general since this does not seem like best practice at all.
Thank you.
Vuex manages all of the data in your application. It's a "single source of truth" for data on your front-end. Therefore, anything that changes the state of your application, such as adding a friend, or denying a friend, needs to flow through Vuex. This happens through three main function types, getters, actions, and mutations.
Check out: https://github.com/vuejs/vuex/tree/master/examples/shopping-cart/vuex
Getters are used to fetch data from storage in Vuex. They are reactive to changes, meaning if Vuex data changes, the information in your component is updated as well. You can put these in something like getters.js so that you can import them in any module you need them in.
Actions are functions that you call directly, ie. acceptFriendRequest when a user clicks the button. They interact with your database, and then dispatch mutations. In this app, all of the actions are in actions.js.
So you'd call this.acceptFriendRequest(recipient) in your component. This would tell your database to update the friend status, then you get a confirmation back that this happened. That's when you dispatch a mutation that updates the current users' list of friends within Vuex.
A mutation updates the data in Vuex to reflect the new state. When this happens, any data you are retrieving in a getter is updated as well. Here is an example of the entire flow:
import {addFriend} from './actions.js';
import {friends} from './getters.js';
new Vue({
vuex:{
getters:{
friends
}
},
methods:{
addFriend
}
}
store.js:
export default {
state:{
friends: []
},
mutations:{
ADD_FRIEND(state, friend) {
state.friends.push(friend);
}
}
}
actions.js:
export default {
addFriend(friend){
Vue.http.post('/users/1/friends',friend)
.then((response)=>{
dispatch("ADD_FRIEND", response) //response is the new friend
})
}
}
getters.js
export default {
friends(state) {
return state.friends;
}
}
So all of these are organized into their own files, and you can import them in any component you need. You can call this.addFriend(friend) from any component, and then your getter which is accessed from this.friends will automatically update with the new friend when the mutation happens. You can always use the same data in any view in your app and know that it is current with your database.
Some misc stuff:
getters automatically receive state as a variable, so you can always reference the state of your Vuex store
mutations should never be asynchronous. Do fetching/updating in actions and then dispatch mutations just to update your data
creating services (or resources) using Vue Resource will make fetching/updating/deleting resources even easier. you can put these in separate files and import them in your actions.js to keep the database retrieval logic separated. Then you'd be writing something like FriendService.get({id: 1}) instead of Vue.http.get('/users/1'). see https://github.com/vuejs/vue-resource/blob/master/docs/resource.md
Vuex works with vue devtools for "time-traveling". You can see a list of every mutation that has taken place and rewind them/redo them. It's great for debugging and seeing where data is being changed.
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 have been writing components in React.js recently. I have never had to use methods like componentWillMount and componentDidMount.
render is indispensable. getInitialState and other helper methods I wrote also come in handy. But not the two aforementioned lifecycle methods.
My current guess is that they are used for debugging? I can console.log out inside them:
componentWillMount: function() {
console.log('component currently mounting');
},
componentDidMount: function() {
console.log('component has mounted');
}
Are there any other uses?
componentDidMount is useful if you want to use some non-React JavaScript plugins. For example, there is a lack of a good date picker in React. Pickaday is beautiful and it just plain works out of the box. So my DateRangeInput component is now using Pickaday for the start and end date input and I hooked it up like so:
componentDidMount: function() {
new Pikaday({
field: React.findDOMNode(this.refs.start),
format: 'MM/DD/YYYY',
onSelect: this.onChangeStart
});
new Pikaday({
field: React.findDOMNode(this.refs.end),
format: 'MM/DD/YYYY',
onSelect: this.onChangeEnd
});
},
The DOM needs to be rendered for Pikaday to hook up to it and the componentDidMount hook lets you hook into that exact event.
componentWillMount is useful when you want to do something programatically right before the component mounts. An example in one codebase I'm working on is a mixin that has a bunch of code that would otherwise be duplicated in a number of different menu components. componentWillMount is used to set the state of one specific shared attribute. Another way componentWillMount could be used is to set a behaviour of the component branching by prop(s):
componentWillMount() {
let mode;
if (this.props.age > 70) {
mode = 'old';
} else if (this.props.age < 18) {
mode = 'young';
} else {
mode = 'middle';
}
this.setState({ mode });
}
componentDidMount only runs once and on the client side. This is important, especially if you're writing an isomorphic app (runs on both the client and server). You can use componentDidMount to perform tasks require you to have access to window or the DOM.
From Facebook's React Page
If you want to integrate with other JavaScript frameworks, set timers using setTimeout or setInterval, or send AJAX requests, perform those operations in this method.
componentWillMount has fewer use cases (I don't really use it), but it differs in that it runs both on the client and server side. You probably don't want to put event listeners or DOM manipulations here, since it will try to run on the server for no reason.
This is an example of an isomorphic web application that makes use of componentWillMount: https://github.com/coodoo/react-redux-isomorphic-example
However, I'm 99% certain that it runs the code inside componentWillMount for no reason on the server side (I think using componentDidMount to ensure it was only run client side would have made more sense) as the code which ensures that fetch promises are fulfilled before rendering the page is in server.js not inside the individual components.
There is discussion on per-component async fetching here: https://github.com/facebook/react/issues/1739 As far as I can tell, there is not a good use case for componentWillMount as far as isomorphism is concerned at least.
In my project which is a dashboarding tool, I have used componentDidMount().
On home page previously saved dashboards appear on the sidebar. I make an ajax call within componentDidMount() of component rendering Homepage, so as to fetch list of dashboards asynchronously after the page has been rendered.
Why React Life Cycle Methods?
Intend to use third-party (Ex D3.js) library with React Component
class Example extends React.component{
constructor(){
// init state
// will be called only once
}
componentWillMount(){
// will be called only once
// will not be triggered when re-rendering
// usually will fetch data that is needed in order
// to render properly from other API
}
shouldComponentUpdate(){
return false
// will not re-render itself after componentDidMount(){}
}
render(){
return (
<div id="chart"></div>
)
}
componentDidMount(){
d3.select(".chart")
.selectAll("p")
// d3.js ........
// d3.js ........
// Usually, this will trigger React to re-render itself,
// but this time will not because we have set
// shouldComponentUpdate to false
}
}
Why React wants to do this?
Since rendering DOM is an expensive operation, React uses the layer of virtual DOM to update only DOM / DOMs that is/are different from previous state.