react-import-style
Version:
React hoc, use for combine style.
190 lines (149 loc) • 5.4 kB
JavaScript
import React, { Component } from 'react'
import hoistStatics from 'hoist-non-react-statics'
/*
ImportStyle 适用于普通组件
ImportStyleRoot 适用于最外层组件
*/
class StyleContainer extends Component {
static contextTypes = {
appendStyle: React.PropTypes.func,
getStyle: React.PropTypes.func
}
render () {
const styles = this.context.getStyle()
let styleTagsString = ''
for(let name in styles){
let id = name
let s = removeStyleDot(styles[name].css)
styleTagsString += `<style id=${id}>${s}</style>`
}
return (
<div id="styleCollection" dangerouslySetInnerHTML={{__html: styleTagsString}}></div>
)
}
}
export const ImportStyle = (styles) => (StyleWrappedComponent) => {
class ImportStyle extends Component {
static contextTypes = {
appendStyle: React.PropTypes.func,
removeStyle: React.PropTypes.func
}
constructor (props, context) {
super(props, context)
this.state = {}
this.classNameList = []
this.styles = {}
}
componentWillMount () {
styles = stylesHandleWapperCssLoader(styles)
styles.forEach((style) => {
this.classNameList.push(style.wrapper)
})
this.context.appendStyle(styles)
}
componentWillUnmount () {
this.context.removeStyle(styles)
}
render () {
const props = {
...this.props,
...this.state
}
return (
<StyleWrappedComponent {...props} className={this.classNameList.join(' ')}>
{this.props.children}
</StyleWrappedComponent>
)
}
}
return hoistStatics(ImportStyle, StyleWrappedComponent)
}
export const ImportStyleRoot = () => (StyleWrappedComponent) => {
class ImportStyleRoot extends Component {
constructor (props) {
super(props)
this.styleMap = {}
this.checkAndWriteStyleToHeadTag = () => {
for( let key in this.styleMap){
let styleObj = this.styleMap[key]
if(styleObj.ref > 0){
// 配置样式
if(!document.getElementById(key)){
let styleTag = document.createElement('style')
styleTag.innerHTML = removeStyleDot(styleObj.css)
styleTag.setAttribute('id', key)
document.getElementsByTagName('head')[0].appendChild(styleTag)
}
} else {
// 移除样式
if(document.getElementById(key)){
document.getElementById(key).remove()
}
}
}
}
}
static childContextTypes = {
appendStyle: React.PropTypes.func,
removeStyle: React.PropTypes.func,
getStyle: React.PropTypes.func
}
getChildContext = function () {
return {
appendStyle: (styles) => {
styles.forEach((style) => {
if(!this.styleMap[style.wrapper]){
this.styleMap[style.wrapper] = {
css: style.css,
ref: 1
}
}else{
// 样式引用计数
this.styleMap[style.wrapper].ref ++
}
})
__CLIENT__ && this.checkAndWriteStyleToHeadTag()
},
removeStyle: (styles) => {
styles.forEach((style) => {
// 引用计数减少
if(this.styleMap[style.wrapper]){
this.styleMap[style.wrapper].ref --
}
})
},
getStyle: () => {
return this.styleMap
}
}
}
render () {
const props = {
...this.props,
...this.state
}
return (
<StyleWrappedComponent {...props}>
{this.props.children}
{ __SERVER__ && <StyleContainer />}
</StyleWrappedComponent>
)
}
}
return ImportStyleRoot
}
// 统一处理,把string,object 都转化成array
const stylesHandleWapperCssLoader = (styles) => {
// 如果是对象
if (typeof styles === 'object' && !styles.length) {
styles = [styles]
}
if (typeof styles === 'object' && styles.length) {
return styles
}
throw 'stylesHandleWapperCssLoader() styles type must be array or object'
}
// 删除样式字符前后引号
// const removeStyleDot = (css) => css.substr(1, css.length - 2)
// 新版本的 wapper-style-loader 已经调,不需要做这步处理
const removeStyleDot = (css) => css