I've been playing around with React and Azure App Insights.
const appInsights = useAppInsightsContext();
For Events and Metrics only, there seems to be 2 ways of doing things. Why is this? And why is it only for these 2 things only ie for PageViews or exceptions you can only use the second way (appInsights.trackPageView, appInsights.trackException)
//first way
const trackEventHook = useTrackEvent(
appInsights,
"AppInsightsPage Track Event Hook",
{ extraData: "some extra data important to this" },
false
);
trackEventHook({ extraData: "function call extra data" });
//2nd way
appInsights.trackEvent({ name: "AppInsightsPage Custom Event" }, undefined);
While using Application Insight, we use TrackEvent in our code to count various events. How often users choose a particular feature or maybe how often they make particular choices.
For Example, we want to understand the user behavior on a site and we want to know about specific actions like clicking the Add to Cart button.
This can be done by two ways :
Using trackEvent Method
appInsights.trackEvent({ name: 'EventName', properties: { anyProperty } })
We use appInsights object that we are exporting and pass some data to trackEvent, the name of the event we are tracking and any custom properties we want to include in the event.
Using React Plugin useTrackEvent Hook
const trackEventName = useTrackEvent(appInsights, "Event Name", condition);
The useTrackEvent Hook is used to track any custom event that an application may need to track, such as a button click or other API call. It takes four arguments:
Application Insights instance (which can be obtained from the useAppInsightsContext Hook).
Name for the event.
Event data object that encapsulates the changes that has to be tracked.
skipFirstRun (optional) flag to skip calling the trackEvent call on initialization. Default value is set to true.
trackExpection is used to log exception which are related to API, we don't know when they will happen and for trackPageView, page view telemetry is sent by default when each screen or page is loaded. So, in trackExpection and trackPageView we don't have any data object to track any changes. That's why we don't use useTrackEvent hook for this two.
For more information please check the following Microsoft Documents:
React Plugins for Application Insights.
Application Insight API.
Related
I'm trying to create a payment screen in React using Recurly.js. I'm using the official React library and am trying to hook into the set.plan event to retrieve plan information. I previously did this without issue using vanilla Recurly.js in Angular, but I haven't been able to do so in React and it's not clear to me if I'm doing something wrong or if there's a bug.
I adapted one of the Interactive Demo scenarios to demonstrate this. Please see the comments added in my sandbox:
const [{ price, loading }, setPricing, checkoutPricing] = useCheckoutPricing(
null,
setRecurlyError
);
useEffect(() => {
/*
Should this be called frequently? I know the price state gets updated, but I would not
expect to constantly receive a new/updated instance of checkoutPricing.
*/
console.log("checkoutPricing updated", checkoutPricing);
/*
This never gets called even after selecting a new plan in the dropdown menu
*/
checkoutPricing.on("set.plan", (plan) => {
console.log("plan updated", plan);
});
}, [checkoutPricing]);
Am I getting a new instance of checkoutPricing on each render? That might explain why my event is never firing, but I'm not sure how to avoid that.
easy enough right : )
-react app
-im using import TagManager from "react-gtm-module";
in root.js
useEffect(() => {
TagManager.initialize({ gtmId: gtmKey });
//..........
},[]);
i show gtm.js firing in the network tab on browser tools, this should mean i'm running
now in my component, trying to access data layer
const GTMSearchEvent = (v) => {
console.log(v);
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({
"event": "search",
"searchQuery": v,
});
};
my param "v" is logging so the function is running, just not really able to see any event triggering
i cant seem to find anything in GTM or GA showing this event.
how can i finish implementing? just want to pass the search queries to my analytics/tag mgr
Setting up the dataLayer pushes is just half of the battle. You need to config your GTM container to "listen" for those events and variables.
Setup new variable to track search query (note that you need to name it exactly how you pushed it to the data layer.
Setup new trigger to track the data layer event
After that, you can do whatever you want/need to do. Here's a simple example if you want to capture it as an event (note, the names are arbitrary, pick something that makes sense to you, just note the variable name that I used for the label, you can use this anywhere within GTM.
So I am trying to work with FullCalendar and I want to have the user be able to edit an event details, click update and I locally update the event and then push it up to the server. I have the following code, but the issue is when there are multiple changes, is calling the event callback multiple times. Is there a way I could do this just once to save on many API calls? Here is the code I have
let currentEvent = calendarApi.getEventById(eventId);
currentEvent.setExtendedProp('notes', notes);
currentEvent.setExtendedProp('person', person);
Maybe there is a different method I am just not seeing in the docs?
The undocumented mutate method that's used to implement setExtendedProp accepts an object that can have multiple properties. You could use it like this:
event.mutate({
extendedProps: {
notes: notes,
person: person,
},
})
or, using object property value shorthand:
event.mutate({extendedProps: {notes, person}})
I have absolutely no experience with fullcalender though, so use at your own risk!
I'm trying to replace a Backbone.Marionette App to React and am facing difficulty thinking about query params. I think I'm missing a really simple peace in understanding this pattern so I apologize if this question is totally nonsense. I would appreciate any support or just pointing me to some direction that I can google more specifically.
There's a /users page which lists users and you can filter the users via search bar. So if you want to filter the users which contain 'joe' in their username, I would make a request to the server with query params like /users?username=joe. In addition I am able to paginate by adding a page parameter, too (/users?username=joe&page=1).
If I only think about the functionality, the flow would probably be
The Client inserts joe to the input element and clicks Search.
Clicking the Search button fires an Action (like Action.getUser).
The Action makes a request to the server and receives the results
The Dispatcher dispatches a function with the results payload to whomever (usually the Store) is interested in the Action.
The Store's state changes with the new result received by the Action
The View (Component) re-renders by listening to the Store's change.
and it works as expected. However, I would like the Client to be able to bookmark the current filtered result and be able to come back to the same page some time later. This means I will need somewhere to save explicit information about the search term the Client made, which is usually the url (am I right?). So I will need to update the url with query parameters to save the search term (/users?username=joe&page=1).
What I'm confused is where and when to update the url? What I can come up with right now are the 2 options below - and they don't seem to be clean at all.
Option 1
The Client inserts joe to the input element and clicks Search.
Clicking the Search button fires a transition of the ReactRouter with the new query params (/users?username=joe&page=1).
The View (Component) receives the new params via this.props.params and this.props.query.
The View (Component) fires an Action like Action.getUser depending on the query params it receives - in this case username=joe&page=1.
after this, it is the same as above
Option 2 (only 6 is different from what I explained above)
The Client inserts joe to the input element and clicks Search.
Clicking the Search button fires an Action (like Action.getUser).
The Action makes a request to the server and receives the results
The Dispatcher dispatches a function with the results payload to whomever (usually the Store) is interested in the Action.
The Store's state changes with the new result received by the Action
The View (Component) re-renders by listening to the Store's change. And somehow (I don't know how, yet) updates its url depending on its props (like this.props.searchusername, and this.props.searchpage)
What is the best practice on handling query params? (or this may not be specific to query params)
Am I completely misunderstanding the design pattern or architecture? Thanks in advance for any support.
Some articles I've read
Any way to get current params or current query from router (outside of component)?
Async data and Flux stores
Make it easier to add query parameters
React Router and Arbitrary Query Params: Page Refreshes Unintentionally on Load?
Add default params?
I would consider best practice to be the submit button only setting the location query (username). The rest should be taken care by the main react component that is assigned as router component. By this, you can be sure that anytime one revisits or shares the url, they can get the same results. And this is very generic too.
Something like this:
let myQuery = this.props.location.query;
if (myQuery.username) {
let theUser = myQuery.username;
this.setState({
userName = myQuery.username
});
} else {
this.setState({
userName = false //Show All
});
}
And then use this state "userName" to send to the server to search with. By this way, you will not need to iterate the code of the component that takes care of listing users since server already sends the relevant data.
In my experience with using location queries in React, I have been very content with their reactivity cycles and performance. I'd highly recommend keeping every different app state in relevance with the url.
Not entirely sure what you mean by
this means I will need to update the url to save the information (/users?username=joe&page=1).
You will probably have a similar structure to this.
TopContainer.jsx
-- Users.jsx
-- a list of User.jsx
Usually TopContainer will watch all the stores and if anything changed, pass it down to users.jsx. That way in Users.jsx, you can simply render this.props.users without worrying about any reRendering.
The search users actions usually happens in TopContainer's componentWillMount event, and you the page will listen to UserStore. That's a good place to throw in any query params. Something like this would work
componentWillUnmount() {
let searchTerm = router.getCurrentQuery().searchTerm;
UserActions.searchUsers(searchTerm)
},
The page doesn't really care if the url has a query params or not, it just dumbly shows whatever in the user store.
Then when the search finishes, Users.jsx will be reloaded and show the correct results
I have read in several places that calling the Backbone.history.navigate function is considered bad practice.
For example Addy Osmani sais in his book "Developing Backbone.js Applications"
It is also possible for Router.navigate() to trigger the route along
with updating the URL fragment by passing the trigger:true option.
Note: This usage is discouraged...
http://addyosmani.github.io/backbone-fundamentals/#backbone.history
Or Derick Bailey in his blog post even sais:
You shouldn’t be executing the route’s handler from within your application, most of the time.
But I don't really understand the reasoning behind it and what would be a better solution.
In my opinion it is not really bad to call the navigate function with the trigger:true option. The route function could upon calling always check if the considered data is already loaded and show this loaded data instead of doing the whole work all over again...
There seems to be some confusion about what Router#navigate does exactly, I think.
Without any options set it will update the URL to the fragment provided.
E.g. router.navigate('todo/4/edit') will update the URL to #todo/4 AND will create a browser history entry for that URL. No route handlers are run.
However, setting trigger:true will update the URL, but it will also run the handler that was specified for that route (In Addy's example it will call the routers editTodo function) and create a browser history entry.
When passing replace:true the url will be updated, no handler will be called, but it will NOT create a browser history entry.
Then, what I think the answer is:
the reason why the usage of trigger:true is discouraged is simple, navigating from application state to application state to application state requires most of the time different code to be run than when navigating to a specific application state directly.
Let's say you have states A, B and C in your application. But state B builds upon state A and state C builds upon B.
In that case when you navigate from B to C only a specific part of code will need to be executed, while when hitting state C directly will probably execute some state checking and preparation:
has that data been loaded? If not, load it.
is the user logged in? If not redirect.
etc.
Let's take an example: State A (#list) shows a list of songs. State B (#login) is about user authentication and state C (#list/edit) allows for editing of the list of songs.
So, when the user lands on state A the list of songs is loaded and stored in a collection. He clicks on a login-button and is redirected to a login form. He successfully authenticates and is redirected back to the song list, but this time with delete-buttons next to the songs.
He bookmarks the last state (#list/edit).
Now, what needs to happen when the user clicks on the bookmark a few days later?
The application needs to load the songs, needs to verify the user is (still) logged in and react accordingly, stuff that in the state transition flow had already been done in the other states.
Now for a note of my own:
I'd never recommend the above approach in a real application as in the example. You should check whether the collection is loaded when going from B to C and not just assume it already is. Likewise you should check whether the user really is logged in. It's just an example.
IMO the router really is a special kind of view (think about it, it displays application state and translates user input into application state/events) and should always be treated as such. You should never ever rely on the router to transition between states, but rather let the router reflect the state transitions.
I have to disagree with #Stephen's answer here. And the main reason why is because the use of router.navigate({trigger : true}) gives the router responsibility to handle the application's state. It should only reflect application state, not control it.
Also, it is not a View's responsibility to change the hash of the window, this is the router's only job! Don't take it away from it! Good modularity and separation of concerns makes for a scalable and maintainable application.
Forwarding a person to a new section within your application
Backbone is an event driven framework, use events to communicate. There is absolutely no need to call router.navigate({ trigger : true }) since functionality should not be in the router. Here is an example of how I use the router and I think promotes good modularity and separation of concerns.
var Router = Backbone.Router.extend({
initialize: function(app) {
this.app = app;
},
routes: {
'videoLibrary' : function() { this.app.videoLibrary(); }
}
});
var Application = _.extend({}, Backbone.Events, {
initialize: function() {
this.router = new Router( this );
this.listenTo( Backbone, 'video:uploaded', function() {
this.router.navigate('/videoLibrary');
this.videoLibrary();
});
},
videoLibrary: function() {
//do useful stuff
}
});
var uploadView = Backbone.View.extend({
//...
uploadVideo: function() {
$.ajax({
//...
success: function() { Backbone.trigger('video:uploaded'); }
});
}
});
Your view does not need or want to know what to do when the user is done uploading, this is somebody else's responsibility. In this example, the router is just an entry point for the application's functionality, an event generated by the uploadView is another. The router always reflects the application state through hash changes and history but does not implement any functionality.
Testability
By separating concerns, you are enhancing the testability of your application. It's easy to have a spy on Backbone.trigger and make sure the view is working properly. It's less easy to mock a router.
Modules management
Also, if you use some module management like AMD or CommonJS, you will have to pass around the router's instance everywhere in the application in order to call it. Thus having close coupling in your application an this is not something you want.
In my opinion it's considered bad practice because you should imagine a Backbone application not like a Ruby On Rails application but rather like a Desktop application.
When I say RoR, I'm just saying a framework supporting routing in sense that a route brings you to a specific call to the controller to run a specific action (imagine a CRUD operation).
Backbone.history is intended just as a bookmark for the user so he can, for example, save a specific url, and run it again later. In this case he will find the same situation he left before.
When you say:
In my opinion it is not really bad to call the navigate function with
the trigger:true option. The route function could upon calling always
check if the considered data is already loaded and show this loaded
data instead of doing the whole work all over again...
That to me sounds smelly. If you are triggering a route and you are checking for the data to see if you have it, it means that you actually already had them so you should change your view accordingly without loading again the entire DOM with the same data.
That said trigger:true is there so do we have reason use it? In my opinion it is possible to use it if you are completely swapping a view.
Let's say I have an application with two tabs, one allows me to create a single resource, the other one let me see the list of the created resources. In the second tabs you are actually loading a Collection so data is different between the two. In this case I would use trigger:true.
That said I've been using Backbone for 2 weeks so I'm pretty new to this world but to me it sounds reasonable to discourage the use of this option.
It depends on your context.
If you have done something in your current view that might affect the view you are about to navigate to, for example creating for deleting a customer record, then setting trigger to true is the right thing to do.
Think about it. If you delete a customer record don't to want to refresh the list of customers to reflect that deletion?