Skip to content

Commit

Permalink
feat: simple mods optimization in compose
Browse files Browse the repository at this point in the history
BREAKING CHANGE: changed compose order for simple mods, classnames -> classname in simple mods
  • Loading branch information
sergcen committed Jul 27, 2020
1 parent 003c0e0 commit 60c2ee7
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 3 deletions.
62 changes: 60 additions & 2 deletions packages/core/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ export function withBemMod<T, U extends IClassNameProps = {}>(
let entityClassName: string
let modNames: string[]

return function WithBemMod<K extends IClassNameProps = {}>(
const withMod = function WithBemMod<K extends IClassNameProps = {}>(
WrappedComponent: ComponentType<T & K>,
) {
// Use cache to prevent create new component when props are changed.
Expand All @@ -70,6 +70,7 @@ export function withBemMod<T, U extends IClassNameProps = {}>(

function BemMod(props: T & K) {
modNames = modNames || Object.keys(mod)

// TODO: For performance can rewrite `every` to `for (;;)`.
const isModifierMatched = modNames.every((key: string) => {
const modValue = mod[key]
Expand Down Expand Up @@ -125,6 +126,12 @@ export function withBemMod<T, U extends IClassNameProps = {}>(

return BemMod
}

withMod.blockName = blockName
withMod.mod = mod
withMod.isSimple = !enhance

return withMod
}

export type ExtractProps<T> = T extends ComponentType<infer K> ? { [P in keyof K]: K[P] } : never
Expand All @@ -134,6 +141,47 @@ export type Composition<T> = <U extends ComponentType<any>>(
fn: U,
) => StatelessComponent<JSX.LibraryManagedAttributes<U, ExtractProps<U>> & T>

type AllMods = {
[key: string]: string[]
}

export function composeSimple(mods: any[]) {
const { blockName } = mods[0]
const allMods: AllMods = {}

const entity = cn(blockName)

for (let index = 0; index < mods.length; index++) {
const { mod } = mods[index]

Object.keys(mod).forEach((key) => {
allMods[key] = allMods[key] || []

allMods[key].push(mod[key])
})
}
const modNames = Object.keys(allMods)

return (Base: any) => {
return (props: any) => {
const modifiers = modNames.reduce((acc: NoStrictEntityMods, key: string) => {
const modValues = allMods[key]
const propValue = (props as any)[key]

if (modValues.includes(propValue)) {
acc[key] = propValue
}

return acc
}, {})

const className = entity(modifiers, [props.className])

return createElement(Base, { ...props, className })
}
}
}

export function compose<T1>(fn1: HOC<T1>): Composition<T1>

export function compose<T1, T2>(fn1: HOC<T1>, fn2: HOC<T2>): Composition<T1 & T2>
Expand Down Expand Up @@ -208,7 +256,17 @@ export function compose() {
// Use arguments instead of rest-arguments to get faster and more compact code.
const fns: any[] = [].slice.call(arguments)

return fns.reduce(
const simple = []
const enhanced = []
for (let index = 0; index < fns.length; index++) {
const f = fns[index]

f.isSimple ? simple.push(f) : enhanced.push(f)
}

const oprimizedFns = simple.length ? [composeSimple(simple), ...enhanced] : enhanced

return oprimizedFns.reduce(
(a, b) => {
return function() {
return a(b.apply(0, arguments))
Expand Down
12 changes: 11 additions & 1 deletion packages/core/test/compose.test.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { FC, ComponentType } from 'react'
import { mount } from 'enzyme'

import { compose, composeU } from '../core'
import { compose, composeU, withBemMod } from '../core'

type BaseProps = {
text: string
Expand All @@ -23,13 +23,19 @@ type SizeAProps = {
size?: 'a'
}

type SimpleProps = {
simple?: true
}

const Component: FC<BaseProps> = ({ children }) => <div>{children}</div>
const withSimpleCompose = withBemMod<SimpleProps>('EnhancedComponent', { simple: true })
const withHover = (Wrapped: ComponentType<any>) => (props: HoveredProps) => <Wrapped {...props} />
const withThemeA = (Wrapped: ComponentType<any>) => (props: ThemeAProps) => <Wrapped {...props} />
const withThemeB = (Wrapped: ComponentType<any>) => (props: ThemeBProps) => <Wrapped {...props} />
const withSizeA = (Wrapped: ComponentType<any>) => (props: SizeAProps) => <Wrapped {...props} />

const EnhancedComponent = compose(
withSimpleCompose,
withHover,
withSizeA,
composeU(withThemeA, withThemeB),
Expand All @@ -47,4 +53,8 @@ describe('compose', () => {
test('should compile component with hovered true', () => {
mount(<EnhancedComponent hovered text="" />)
})

test('should compile component with simple mod', () => {
mount(<EnhancedComponent theme="b" simple text="" />)
})
})

0 comments on commit 60c2ee7

Please sign in to comment.