Skip to content

Instantly share code, notes, and snippets.

@gregnb
Created June 6, 2017 01:47
Show Gist options
  • Save gregnb/b2c1ec35d9f19add404c7f1d00d6b7a1 to your computer and use it in GitHub Desktop.
Save gregnb/b2c1ec35d9f19add404c7f1d00d6b7a1 to your computer and use it in GitHub Desktop.
import React from 'react';
import { apiRequest, apiError, detectAndroid } from './Utils';
let FrontendComponent = ComposedComponent => {
class FrontendClass extends ComposedComponent {
constructor() {
super();
/* global vars needed */
this.metaAttribute = 'data-stylesheet-dynamic';
this.dynamicStyleSheets = [];
this.loadedStyleSheets = [];
const initialState = {
dataRefresh : 0,
cssLoaded : false
};
this.state = (this.state) ? Object.assign(this.state, initialState) : initialState;
}
componentDidMount() {
if(this.metaConfig)
this.updateMetaData();
if(super.componentDidMount)
super.componentDidMount();
}
shouldComponentUpdate(nextProps, nextState, nextContext) {
return (this.context != nextContext || this.props != nextProps || this.state != this.nextState)
? true
: false;
}
/*
* API CALL
*/
dataRequest(url, data, callback) {
let self = this;
apiRequest(url, data).then(function(result) {
if(callback)
callback(result);
}).catch(function(error) {
apiError(error);
});
}
toggleState(option, toggle) {
let newstate = {};
newstate[option] = toggle;
this.setState(newstate);
}
refreshData() {
this.setState({ dataRefresh:this.state.dataRefresh+1 });
}
render() {
/*
* FOUC - Flash of Unstyled Content (https://en.wikipedia.org/wiki/Flash_of_unstyled_content)
* We tag an onload/onerror event to the dynamic injected stylesheets. If onload/onload count matches total sheets then
* we know everything was attempted
*
*
* Onload: Firefox 9+, IE6+, Edge, Chrome 19+, Safari 6.01+
* OnError: Firefox 9+, IE6+, Edge, Chrome 19+, Safari 6.0+,
*
* No Android support, how do we solve that? Skip for now.
* http://www.backalleycoder.com/2011/03/20/link-tag-css-stylesheet-load-event/
*
*
* Browser Support: https://pie.gd/test/script-link-events/
*/
if(!this.state.cssLoaded && this.metaConfig && !detectAndroid())
return false;
return super.render();
}
/*
* document.head [meta,title,scripts,links] routines
*/
getAttributes(el) {
let attributes = Array.prototype.slice.call(el.attributes);
let data = {};
for(let i = 0; i < attributes.length; i++) {
let obj = attributes[i];
data[obj.nodeName] = obj.nodeValue;
}
return (data) ? data : null;
}
deleteMetaData() {
let metaData = this.metaConfig();
let configSheets = metaData.link
if(!configSheets)
return;
let headEl = document.head || document.querySelector("head");
let styleSheets = this.listStyleSheets();
configSheets.forEach((link, index) => {
styleSheets.forEach((sheet, index) => {
if(sheet.getAttribute('href') == link.href)
headEl.removeChild(sheet);
});
});
}
listStyleSheets() {
let headEl = document.head || document.querySelector("head");
let checkEl = headEl.querySelectorAll('link['+this.metaAttribute+']');
let sheets = [];
if(checkEl)
for(let i = 0; i < checkEl.length; i++)
sheets.push(checkEl[i]);
return sheets;
}
checkStylesheetsLoaded() {
if(this.dynamicStyleSheets.length == this.loadedStyleSheets.length) {
console.log("stylesheets all loaded!");
//this.setState({ cssLoaded: true });
} else {
console.log("still not loaded");
}
console.log(this.state);
}
updateMetaData(data) {
let metaData = (this.metaConfig) ? this.metaConfig() : null;
let headEl = document.head || document.querySelector("head");
let self = this;
/* diffing maybe not needed? lol */
if(data)
//metaData = objDiff(data,metaData);
metaData = data;
document.title = metaData.title || document.title;
if(metaData.meta && metaData.meta.length) {
let nodes = headEl.querySelectorAll('meta');
let tags = (nodes) ? Array.prototype.slice.call(nodes) : null;
let oldTags = [];
let newTags = [];
metaData.meta.forEach((meta, index) => {
let newEl = document.createElement('meta');
for(let prop in meta)
if(prop != 'content')
newEl.setAttribute(prop, meta[prop]);
tags.forEach((tag) => {
var tagCheck = tag.cloneNode();
tagCheck.removeAttribute('content');
if(tagCheck.isEqualNode(newEl))
oldTags.push(tag);
});
if(meta.content)
newEl.setAttribute('content', meta.content);
newTags.push(newEl);
});
/* delete old/duplicate tags */
oldTags.forEach((tag) => {
headEl.removeChild(tag);
});
/* set new tags */
newTags.forEach(function(tag) {
headEl.appendChild(tag);
});
}
/*
* get list of current stylesheets.
* if exists, don't append to <head>.
*
* if no stylesheets are provided just set cssLoaded : true
*
*/
if(metaData.link) {
let stylesheets = this.listStyleSheets();
let appendCheck = 0;
this.loadedStyleSheets = [];
this.dynamicStyleSheets = metaData.link;
this.setState({ cssLoaded: false });
metaData.link.forEach((link, index) => {
let newEl = document.createElement('link');
let append = 1;
stylesheets.forEach((sheet, index) => {
if(sheet.getAttribute('href') == link.href)
append = 0;
});
if(!append) {
appendCheck++;
return;
}
for(let prop in link)
newEl.setAttribute(prop, link[prop]);
/* set onLoad/onError for stylesheets */
let cssLoad = () => {
let link = newEl.getAttribute('href');
this.loadedStyleSheets.push(link);
if(this.dynamicStyleSheets.length == this.loadedStyleSheets.length)
self.setState({ cssLoaded: true });
};
newEl.onload = cssLoad;
newEl.onerror = cssLoad;
newEl.setAttribute(this.metaAttribute, true);
headEl.appendChild(newEl);
});
/*
* incase a developer tries to load in already loaded CSS stylesheets we're going to set cssLoaded to true
*/
if(appendCheck == metaData.link.length)
this.setState({ cssLoaded: true });
} else {
this.setState({ cssLoaded: true });
}
}
}
FrontendClass.contextTypes = {
params: React.PropTypes.array,
root: React.PropTypes.string,
path: React.PropTypes.string,
queryString: React.PropTypes.string,
changeRoute: React.PropTypes.func,
routerHistory: React.PropTypes.func
};
FrontendClass.frontendComponent = true;
return FrontendClass;
}
export default FrontendComponent;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment