Skip to content

Commit

Permalink
Manage CSSRules with "Stylesheet" instance
Browse files Browse the repository at this point in the history
This update refactors the previous implementation to use an instance to
track/manage/interface with `withStyle`, instead of relying purely on a
privately defined CONSTANT.

The UUID mechanism has also been replaced by a simple incrementing ID
mechanism. This nullifies the risk of collision and is simpler :).
  • Loading branch information
ItsJonQ committed May 2, 2018
1 parent 05bca90 commit 0301715
Show file tree
Hide file tree
Showing 5 changed files with 83 additions and 86 deletions.
62 changes: 62 additions & 0 deletions src/StyleSheet/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
const makeStyleSheet = () => {
const state = {
id: 0,
CSSRules: {}
}

const addRule = (id, styles) => {
state.CSSRules[id] = styles
}

const getRule = (id) => {
return state.CSSRules[id]
}

const hasRule = (id) => {
return !!getRule(id)
}

const removeRule = (id) => {
state.CSSRules[id] = undefined
}

const makeRule = (CSSRules) => {
state.id = state.id + 1
return { id: state.id, CSSRules }
}

const makeStyles = (props) => {
return generateStyles(props)
}

return {
addRule,
getRule,
hasRule,
removeRule,
makeRule,
makeStyles,
CSSRules: state.CSSRules
}
}

/**
* Creates the tokenized styles based.
*/
export const generateStyles = ({id, props, CSSRules}) => {
const parsedCSSRules = typeof CSSRules === 'function'
? CSSRules(props) : CSSRules

return tokenize(id, parsedCSSRules)
}

/**
* Renders the CSSRule with tokenized with the unique ID.
*
* @param {string} id
* @param {string} CSSRules
* @returns {string}
*/
export const tokenize = (id, CSSRules) => `/* ${id} */\n${CSSRules.trim()}\n`

export default makeStyleSheet
5 changes: 0 additions & 5 deletions src/utilities/id.js

This file was deleted.

4 changes: 3 additions & 1 deletion src/withStyles/__tests__/withStyles.test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import React from 'react'
import { mount } from 'enzyme'
import withStyles, { removeStyle } from '../index'
import withStyles from '../index'

const removeStyle = withStyles.StyleSheet.removeRule

describe('HOC Composition', () => {
const Button = props => (<button {...props} />)
Expand Down
20 changes: 1 addition & 19 deletions src/withStyles/helpers.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { ID } from './index'
import { uuid } from '../utilities/id'
export const ID = '__REACT_REACTOR_STYLES__'

/**
* Creates the <style> tag, and adds it to the <head>.
Expand Down Expand Up @@ -27,20 +26,3 @@ export const getStyleTag = () => {
const tag = document.getElementById(ID)
return tag || makeStyleTag()
}

/**
* Renders the CSSRule with tokenized with the unique ID.
*
* @param {string} id
* @param {string} CSSRules
* @returns {string}
*/
export const tokenize = (id, CSSRules) => `/* ${id} */\n${CSSRules.trim()}\n`

/**
* Generates the styleProps with uniqueID for withStyles to consume.
*
* @param {string} CSSRules
* @returns {object}
*/
export const makeCSS = (CSSRules) => ({ id: uuid(), CSSRules })
78 changes: 17 additions & 61 deletions src/withStyles/index.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
import React, { Component } from 'react'
import Style from './Style'
import {
getStyleTag,
makeCSS,
tokenize
getStyleTag
} from './helpers'
import { getComponentName } from '../utilities/components'
import makeStyleSheet from '../StyleSheet'

export const ID = '__REACT_REACTOR_STYLES__'
export const MANAGER = {}
export const STYLESHEET = makeStyleSheet()

/**
* HOC that renders specified CSS rules.
Expand All @@ -18,17 +16,22 @@ export const MANAGER = {}
* @returns {React.Component}
*/
const withStyles = (styles) => Composed => {
const { id, CSSRules } = makeCSS(styles)
const { id, CSSRules } = STYLESHEET.makeRule(styles)

class WithStylesComponent extends Component {
constructor (props) {
super(props)
this.styleSheet = STYLESHEET
}

componentDidMount () {
if (!id || !CSSRules || hasStyle(id)) return
if (!id || !CSSRules || this.styleSheet.hasRule(id)) return

const cssStyles = generateStyles({ id, props: this.props, CSSRules })
const cssStyles = this.styleSheet.makeStyles({ id, props: this.props, CSSRules })
const tagNode = getStyleTag()
tagNode.innerHTML += cssStyles

addStyle(id, cssStyles)
this.styleSheet.addRule(id, cssStyles)
}

componentDidUpdate (prevProps) {
Expand All @@ -38,21 +41,21 @@ const withStyles = (styles) => Composed => {
/* istanbul ignore next */
if (prevProps === this.props) return

const prevStyles = getStyle(id)
const prevStyles = this.styleSheet.getRule(id)
/**
* Cannot really tested in Enzyme, as there is always styles whe a
* component is initialized. This is a fail-safe guard.
*/
/* istanbul ignore next */
if (!prevStyles) return

const nextStyles = generateStyles({ id, props: this.props, CSSRules })
const nextStyles = this.styleSheet.makeStyles({ id, props: this.props, CSSRules })
if (prevStyles === nextStyles) return

const tagNode = getStyleTag()
tagNode.innerHTML = tagNode.innerHTML.replace(prevStyles, nextStyles)

addStyle(id, nextStyles)
this.styleSheet.addRule(id, nextStyles)
}

render () {
Expand All @@ -62,61 +65,14 @@ const withStyles = (styles) => Composed => {

WithStylesComponent.displayName = `withStyle(${getComponentName(Composed)})`
WithStylesComponent._withStylesId = id
WithStylesComponent._styleSheet = STYLESHEET

return WithStylesComponent
}

/**
* Creates the tokenized styles based.
*/
export const generateStyles = ({id, props, CSSRules}) => {
const parsedCSSRules = typeof CSSRules === 'function'
? CSSRules(props) : CSSRules

return tokenize(id, parsedCSSRules)
}

/**
* Retrieves the cached styles based on id.
*
* @param {string} id
* @returns {bool}
*/
export const getStyle = (id) => MANAGER[id]

/**
* Checks to the see if the styles have been previously added by ID.
*
* @param {string} id
* @returns {bool}
*/
export const hasStyle = (id) => !!getStyle(id)

/**
* Adds ID to mark that styles have been added.
*
* @param {string} id
* @returns {object}
*/
const addStyle = (id, styles) => {
MANAGER[id] = styles
return MANAGER
}

/**
* Removes an ID from the styles manager.
*
* @param {string} id
* @returns {undefined}
*/
export const removeStyle = id => {
MANAGER[id] = undefined
return MANAGER
}

/**
* Sub-components
*/
withStyles.Style = Style
withStyles.StyleSheet = STYLESHEET

export default withStyles

0 comments on commit 0301715

Please sign in to comment.