npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

react-tmpl

v0.1.6

Published

Abstract boilerplate from writing presentational React

Downloads

16

Readme

React-TMPL

I found myself repeating the same stuff over and over again when creating React templates. This is an attempt at abstracting a bunch of the boilerplate.

What is a template?

The way I work, React components can be separated in two types:

  • Functional
  • Representational Yes, I know this is kind of a throwback to MVC (or at least VC), but there isn't really another way to obtain the following benefits:
  • Switch your templates for React-Native
  • Offer modules that can be seamlessly integrated with material-ui or bootstrap with minimal effort
  • not write a lot of boilerplate to pass down presentational options from components to nested components. I want class names to be concatenated, styles to be merged, recursively and without writing anything.

How it works

just use template(renderFunction,defaultProps)

import tmpl from 'react-tmpl';

tmpl(
	function Button(props){
		if(props.action){
			props.onClick = props.action;
		}
		return (<button {...props} key={key}>{props.text}{this.getTime()}</button>);
	}
,	{
		propTypes:['text']
	,	type:'primary'
	,	className:'button'
	,	getTime(){
			return new Date();
		}
	}
)

Now you can use template.Button. Any property (that is not a function) given in the object will be a defaultProp.

Special keys:

  • state will be the initial state
  • style will be recursively merged
  • css will be recusiverly merged IF the prop includeCSS is true (which allows to develop inline styles, then export them to css)
  • className will be recursively merged. In your render template, they will be automatically converted to a string through classnames
  • propTypes will become the Component's propTypes, and can be an array of keys (in which case, they will all default to PropTypes.any.isRequired). You are also free to pass in a regular propTypes object.
  • name will be used as displayName, and will override the function name (if there was one). Note that name is required.
  • buildLocals is a function you can use to pre-process the locals.
  • initialize is a constructor function to set properties you need
  • style.hover and style.focus will automatically create onMouseEnter, onMouseOut (or onFocus, onBlur) and add keys (state.hover or state.focus). Note that css.hover and css.focus do not trigger this behavior (the assumption being, you do not want to handle those states in javascript if you have those in css).

Additionally:

Any property that is a function will be added to the template's prototype, making it available in the render function Any property that begins with on, such as onResize, onClick and so on, will be scoped to the current object automatically (good old magic)

Any property that begins with a capital letter will:

  • create a method of the same name on the object
  • be considered a template's options, thus overriding the default template options.

Consider the following Component:

tmpl(
	function CloseButton(props){
		return this.Button({type:'secondary'})
	}
,	{
		Button:{
			text:'×'
		}
	,	classname:'close'
	}
)

This component does several things:

  • this.Button() calls the Button template (or throws if Button was not defined). The function is binded to the instance and takes two arguments: optional props, and optional key. This allows to use the function directly in a map (return props.array.map(this.Template))
  • it will merge the props given in the render function ({type:'secondary'}) with the default defined props ({text:'×'})
  • it will merge those props with the Button template default props (type and getTime)
  • the final button will have the classes button close.

Should you want to override this default behavior, make Button a function:

tmpl(
	function CloseButton(props){
		return this.Button({type:'secondary'})
	}
,	{
		Button(givenProps){
			const locals = this.locals;
			const closeButtonProps = locals.props;
			// `givenProps` is what is passed in the render function above ({type:'secondary'})
			// `locals` is the full options object
			// `locals.prop` is the object passed to the render function
		}
	}
)

If you want to include a template without passing options, it's enough to just do:

{
	/*...*/
	Button:{}
	/*...*/
}

To switch a template:

CloseButton.templates.Button = class MyButton extends React.Component{/*...*/}

react-tmpl offers a helper to get data from the current locals: prop(predicate), where predicate can be either a string, or a function. Here's how you would use it:

import tmpl,{prop} from 'react-tmpl'
var ids = 0;
tmpl(
	function CloseButton(props){
		return this.Button({type:'secondary'})
	}
,	{
		initialize(props){
			// here, `props` is what is natively passed to the component
			this.id = ids++
		}
	,	buildLocals(locals){
			locals.props.id = this.slug(props.text+this.id);
			return props;
		}
	,	slug(text){
			return text.replace(/[\s*&%$#]/g,'-');
		}
	,	text:prop((locals)=>locals.props.mini?'×':'close')
	,	Button:{
			text:prop('text')
		,	id:prop('slug')
		}
	}
)

prop('string') is equivalent to props(locals=>locals.props.string).

One last thing to know is that you can namespace your templates:

import {createTemplates} from 'react-tmpl'
const template = createTemplates();

template(/*...use it as usual*/)

Install

npm install react-tmpl

Structure of a React Template

How locals are built

this.locals is rebuilt on every render. It contains, at the minimum, a property this.locals.props wich gets passed to the render function. It may also contain a number of keys for every nested template (e.g., this.locals.Button).

The built process is as follows:

  • this.locals.props.className gets merged from default properties (passed at template creation) and passed props (from a parent template, or from the user)
  • this.locals.props.style gets merged from default properties and passed props
  • Any function that begins with an onX gets added to this.locals (so onClick and company are added to the bundle -- Bear in mind those functions have been scoped to the current instance already at this point). However, if passed props also contain a similarly named onX function, they will overwrite those (which are still accessible in the render function as this.onX)
  • this.locals.props will get merged with passed props, the latter keys overriding the former.
  • this.locals gets through a function processProps, that does nothing (useful for overriding stuff in your templates)
  • this.locals gets 'computed'. That is, every prop call gets resolved. At this point, this.locals is a fully serializable object.
  • this.locals.props.className gets through classnames
  • this.locals get through a function buildLocals, that does nothing. This is equivalent to processProps, the difference being, this is a fully resolved object.

TODO

  • Better documentation
  • Tests
  • Find a way to extract css to a stylesheet