Skip to content

Instantly share code, notes, and snippets.

@faceyspacey
Last active November 14, 2015 11:57
Show Gist options
  • Save faceyspacey/e7498f808876548f394d to your computer and use it in GitHub Desktop.
Save faceyspacey/e7498f808876548f394d to your computer and use it in GitHub Desktop.
Blaze React ("Sideburns") refactoring proposal

####1) FLATTEN COMPILATION PROCESS (remove recursion--Templates should not compile themselves) so this:

replace: function(match, className, code) {
	const markup = ReactTemplate.compile(className, code || ''); 
	return `ReactTemplate["${className}"] = (component, context) => { return (${markup}) }; RT.body("${className}");`
}

becomes:

replace: function(match, className, code) {
	return `ReactTemplate["${className}"] = (component, context) => { return (${markup}) }; RT.body("${className}");`
}

#####a) HELPERS: within ReactTemplate.compile() helpers dont need to be compiled recursisvely within a template anymore:

ReactRegex.forEach(function (obj) {
	markup = markup.replace(obj.regex, obj.replace);
});

the markup within templates can be compiled separately since the only param it needs is markup.

#####b) EVENTS: events currently need the className of the compnent:

static appendEventMap(className, markup) {
    var events = Events.getEvents(className);

...well it doesnt need classNames if it passes over all the code itself, getting the className from template name's. So we do events separately as well.

The general idea is flattening the execution process will reduce complexity a lot for developers. We can code it sequentially now...

####2) MAKE COMPILER EXECUTE SEQUENTIALLY:

instead of:

source = "" + new ReactTemplate(original);
const result = ReactTemplateCompiler.transpile(source, inputFile);

we do this:

source = ReactCompiler.parse(original);
const result = ReactTemplateCompiler.transpile(source, inputFile);
      
ReactCompiler = class {
	parse(code) {
		code = this.injectEventHandlers(code);
		code = this.parseMarkup(code);
		return this.parseTemplates(code);
	}
}

and done. The idea being that developers can way more easily trace the flow of what's going on.

####3) COMBINE RENDER ASSIGNMENT + COMPONENT CREATION:

return `ReactTemplate["${className}"] = (component, context) => {
	return (${markup}) 
}; 
RT.template("${className}");`;

becomes:

return `React.Component.createFromBlaze("template", "${className}", (component, context) => { 
	return (${markup}) ;
});`;

and React.Component.createFromBlaze() (which is our new version of RT.template()) at application runtime assigns our core BlazeReact mixin, this render function, utilizing the correct name + type of course:

React.Component.createFromBlaze = function(type, className, renderFunc) {
  React.components[className] = class extends React.Component {
		type: type,
		mixins: [ReactMeteorData, BlazeReact],
		render: renderFunc
	}
}

...that consolidates like 3 files, and eliminates the need for this function: https://github.com/timbrandin/react-templates/blob/master/exports.js#L79 and related STATIC functions like body in export.js.

AHA! I figured it out!! So RT has both STATIC functions that generate the components (RT.template, RT.body, etc) and "DYNAMIC" ones that run within the markup (RT.get, RT.lookup)

Now that we have started to remove the static ones, lets do the same with the dynamic instance ones--by moving them to our BlazeReact mixin, eg: BlazeReact.prototype.lookup(), which is accessible at this.lookup() in the markup.

This was a core complication that made it confusing. One can't tell what methods are essentially "static" and which ones are "dynamic" in RT within export.js. Now we have moved all these methods to locations that truly make sense.

Specifically, the static ones are now React.Component.createFromBlaze and maybe another one or 2 like that. And the instance ones are essentially on the prototype of React.Component via our BlazeReact mixin. Those 2 locations deeply convey their purpose and the difference between the 2: static vs. dynamic, and maybe more importantly: unrelated to our compilation code!

####4) MOVE DYNAMIC RT METHODS (eg. lookup) + TEMPLATE.JSX METHODS (eg. subscribe) TO BlazeReact MIXIN. We now have instance methods in their correct place coming from 2 places: the old incongruent export.js file containing the RT singleton and methods like this.subscribe already on the resulting component. The only difference for the latter being that they are now more clearly defined on a simple Mixin.


That will give us the foundation we need to focus on regexes and lookup-related instance methods. From there we can work out all the context lookup issues.

@timbrandin
Copy link

This looks great apart from one detail, mixins are moving away from React see: https://facebook.github.io/react/blog/2015/01/27/react-v0.13.0-beta-1.html#mixins

@timbrandin
Copy link

Also by taking away ReactTemplate, I'm afraid we're taking away the possibility to easily override a template.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment