If I want to use Google analytics and at the same time make my site SEO friendly in React, what is the best way?
At the moment I use react-router and a flux pattern where I change the route and then in componentDidMount I fire an action that loads my data through ajax and then updates the store which emits the change and finally I re-render the components that are affected. During the ajax loading I dispatch an event so that my store knows ajax is loading and render a spinner in my component.
I have noticed that when I send my tracking data to Google the ajax has not finished loading and I only send the new page URL not the title or any other data which I load through ajax (I guess this is bad from an SEO perspective and it's definitely bad for my GA data).
This is my GA setup (I use react-ga):
Router.run(routes, Router.HistoryLocation, function(Handler, state) {
ga.pageview(state.pathname);
React.render(<Handler />, document.body);
});
Typical component setup (which allows me to render the correct data based on the URL):
componentDidMount: function() {
ItemStore.addChangeListener(this._onChange);
if(itemSlug) {
ItemActions.loadItemBySlug(this.props.slug);
}
}
I want to be able to have a single point of GA tracking if that is possible. I also want the SEO handling to be correct for the page:
Page title
OG data
H1
etc ...
What is the best approach to solve this?
Should I use the willTransitionTo option in react-router for this? (is it possible to use a loading spinner if I opt for this solution?)
statics: {
willTransitionTo: function (transition, params, query, callback) {
// LOAD AJAX HERE ?
callback();
}
}
How would I go about the willTransistionTo solution in a proper way, can't seem to find any good examples that relate?
Should I add more code, or is it clear what I'm trying to achieve?
Trigger the ga.pageview after the store gets the new data and renders.
The appropriate way to handle SEO in react is to render at the server side isomorphically with React.renderToString, so that any search engine will see the page in its complete state, rather than just Google's spider and only on those occasions when you manually call it.
This was one of the original design criteria of React.
Related
In a Next.js app (full-featured, not next export) that uses React Context for state management and the file-system based router, how can you implement advanced routing?
I want to have preconditions for certain pages, so for instance if you try to load /foo but the Context doesn't have a given property set correctly, it'll route you to /bar.
The actual logic is complex and varies by page, so I'm looking for an approach that's easy to maintain.
Note that these preconditions are not authorization-related, so they do not need to be enforced server-side. It's more like "you need to fill out this form before you can go here."
The use of Context imposes some constraints:
Context must be accessed in a React component or in a custom Hook
Using a custom server for routing is not an option, as that would lose the Context - it has to use client-side routing
The current Context has to be checked (I tried decorating useRouter, but if the Context was changed right before router.push, the custom Hook saw the old values)
Update: It's also good to avoid a flash when the page loads before rerouting happens, so a side goal is to return a loading indicator component in that case.
I believe you can create a HOC and wrapped every pages with you HOC that takes arguments e.g. { redirects: '/foo' }
// pages/bar.tsx
const Page = () => {...}
export default RouteHOC({ redirects: '/foo' })(Page)
the HOC file will be something like this
// hoc/RouteHOC.tsx
const RouteHOC = ({ redirects }) => (WrappedComponent) => {
// you can do your logic here with the context.. even filling up a form here
// too also can.. (like returning a modal first before the real Component).
// useEffect work here too..
const { replace } = useRouter()
// then after you want to replace the url with other page
replace(redirects)
return WrappedComponent
}
This is pretty okay to be maintainable I think. You just create all the logic in HOC and when you want to update the logic - you just have to edit it in 1 file.
Well this is one option I can think of when reading your question - sorry if I misunderstood it in any way. There will always be a better way out there as we all know we can improve and adapt to new situation every seconds :D. Cheers 🥂!!
You can do this.
const Component = () => {
const example = useExample()
return <div id='routes'>
<a href='/example1'>Example 1</a>
{example.whatever && <a href='/example2'>Example 1</a>}
</div>
}
What is best practice to change content on a page without creating a route?
BlazeLayout.render('mainLayout', { top: 'header', menu: 'menu', main: 'dashboard', bottom: 'footer' });
How can i hide/show template components inside the dashboard without creating a new route? Should this be done in helpers using some sort of if/else logic in the html and using helper for on button click? Let's say i want to show different content inside dashboard template based on button clicks (href).
Please provide a best practice and good solution that is easy with lots of components.
How can i hide/show template components inside the dashboard without
creating a new route? Should this be done in helpers using some sort
of if/else logic in the html and using helper for on button click?
You can do that but you should be aware of some points to keep your code clean and modular:
Try to wrap parts of your dashboard into own templates to keep the code clean
Use ReactiveDict in favor of many ReactiveVar instances
Wrap recurring parts in templates, too to reduce duplicate code
Register recurring helpers globally or in the most upper template of your Dashboard
Subscribe on the parent template to data that is shared across all parts of the dashboard and subscribe to local data in the respective components
Use autorun and subscription.ready() and display a loading indicator until the subscription is ready. Don't wait to have everything loaded before rendering as this may reduce the UX dramatically.
Let's say i want to show different content inside dashboard template
based on button clicks (href).
You can attach a data attribute to the button, that has a specific id of the target to be toggled:
<template name="dashboardComponent">
<a href class="toggleButton" data-target="targetId">My Link</a>
</template>
You can then read this id and toggle it's state in your ReactiveDict:
Template.dashboardComponent.events({
'click .toggleButton'(event, templateInstance) {
event.preventDefault();
// get the value of 'data-target'
const targetId = $(event.currentTarget).attr('data-target');
// get the current toggle state of target by targetId
const toggleState = templateInstance.state.get( targetId );
// toggle the state of target by targetId
templateInstance.state.set( targetId, !toggleState );
}
});
In your template you can then ask to render by simple if / else:
<template name="dashboardComponent">
<a href class="toggleButton" data-target="targetId">My Link</a>
{{#if visible 'targetId'}}
<div>target is visible</div>
{{/if}}
</template>
And your helper is returning the state:
Template.dashboardComponent.helpers({
visible(targetName) {
return Template.instance().state.get(targetName);
 }
});
There could be the problem of sharing the state between parent and child templates and I suggest you to avoid Session where possible. However as beginner it is a lot easier to first use Session and then work towards a more decoupled (parameterized templates) solution step by step.
Please provide a best practice and good solution that is easy with
lots of components.
This is a high demand and it is your competency to work towards both! However here is a short peek into this:
Best practice is what works for you plus can work for others in other use cases. Try to share your work with others to see where it will fail for their use case.
Using routes has the advantage, that you can use query parameters to save the current view state in the url. That adds the advantage, that on reloading the page or sharing via link, the page state can be fully restored.
easy with lots of components is a contradiction and I don't know if you expect some magical puff that solves this complexity for you. As a software engineer it is your competency to abstract the complexity into smaller pieces until you can solve the problem within certain boundaries.
I'm currently getting used to using FlowRouter after a while using Iron Router and trying to set up some best practices. I'm subscribing to my collection at a template level.
Previously I've waited for a template to render using onRendered and then targeted my input field and applied focus(), however I am now trying to only show my template in Blaze when the subscriptions are ready using the following (please excuse the Jade but I think it's pretty clear in this case)
template(name="subjectNew")
unless Template.subscriptionsReady
+spinner
else
form
input(type="text" name="name")
So the basic idea is that until the subscriptions are ready the spinner shows. The issue I'm having is that now even when the template renders, the focus won't apply. I've tried various methods of wrapping it in an autorun call but not sure the best way of trying to target the first field when combined with this approach?
Template.subjectNew.onRendered(function() {
console.log('rendered');
$('input').first().focus();
});
Is it possible?
Many thanks for any ideas.
Your subjectNew is considered rendered even when it is only showing the spinner. Just stick your:
form
input(type="text" name="name")
Into a separate template and then attach your focus code to the onRendered handler of that other template.
template(name="subjectNew")
unless Template.subscriptionsReady
+spinner
else
+myForm
template(name="myForm")
form
input(type="text" name="name")
js:
Template.myForm.onRendered(function(){
$('input').focus()
});
I think using an autorun would be a good approach but then you would have to employ Tracker.afterFlush() to wait to set the focus after the form is rendered.
Something like:
Template.subjectNew.onRendered(function() {
this.autorun(() => {
if (this.subscriptionsReady()) {
Tracker.afterFlush(() => $('input').first().focus());
}
});
});
When clicking a link, or loading via console with Router.go(...) there is a noticeable pause between calling the route and my app doing anything.
There are no DDP calls being made during this pause and I added debugging to all my templates for rendering and no re-rendering is taking place. I can't for the life of me figure out what is causing this pause, you can see this in action at http://riustats.com
Bassed on #below9k comment, seems like you are trying to load big amount of data in the desired route.
Be sure don't have some observersChanges also.
A good solution could be using the fast-render package.
Code Example.
Router.route('leaderboard', {
waitOn: function(){
return Meteor.subscribe('leaderboard'); //you big collection.
},
fastRender: true // important to render the route fast.
});
I have an ASP.NET 2.0 Web application.I want to show a progress indcator when user saves some data (Ex : Editing profile).I have already used jQuery in my application for some client side effects. How can i do this ? any jquery trusted stuff to use along with ASP.NET ? Thanks in advance
Do you want to show actual progress or just a busy indicator while the action is happening? If the former, you'll need to have some mechanism to record the save progress in the session and a method to check the state of the progress via AJAX. You'd submit the form via AJAX then periodically call the check method to get reports of the progress and update whatever client-side indicator (usually switch from one to another of a series of canned images or increase the width of some filled "bar"). This, of course, is complicated.
If you want to do the latter, just display an animated GIF that's a busy indicator while you submit the form via AJAX from jQuery using the beforeSend callback, then hide the indicator using the ajax method's complete handler.
$('form').ajax( {
url: '/updateprofile.aspx',
type: 'POST',
data: function() { return $('form').serialize(); },
beforeSend: function() { $('#indicator').show(); },
complete: function() { $('#indicator').hide(); },
success: function(data,status) { alert('Update complete'); }
});
The above code would be in the function invoked from whatever handler invokes the submission or hooked to the form's submit event -- though you'd have to prevent the default action from taking place, too.
An alternative to showing a meaningful progress indicator is to show an animated gif whilst the data is being saved, e.g. the spinning 'daisy' pattern used in Firefox.
This shows the user that something is happening and is usually well received.
Progress indicators which show % complete are often meaningless anyway unless they really have an idea how long the first '50%' will take compared to the last '50%'. Other progress indicators are more meaningful, e.g. those showing record count increments, etc.