diff --git a/src/EnhanceInnerComponent.js b/src/EnhanceInnerComponent.js new file mode 100644 index 0000000..94c968f --- /dev/null +++ b/src/EnhanceInnerComponent.js @@ -0,0 +1,34 @@ +import React from 'react' + +const getDisplayName = wrappedComponent => wrappedComponent.displayName || wrappedComponent.name + +const EnhanceInnerComponent = InnerComponent => { + // const component = InnerComponent + + // const NewInnerComponent = class extends React.Component { + // render() { + // return component(this.props) + // } + // } + // NewInnerComponent.displayName = getDisplayName(component) + + class EnhancerInnerComponent extends InnerComponent { + componentDidMount() { + if (super.componentDidMount) super.componentDidMount() + } + componentDidUpdate() { + if (super.componentDidUpdate) super.componentDidUpdate() + + this.props.sizeMayChange() + } + render() { + return super.render() + } + } + + EnhancerInnerComponent.displayName = `EnhancerInner(${getDisplayName(InnerComponent)})` + + return EnhancerInnerComponent +} + +export default EnhanceInnerComponent diff --git a/src/SizeFetcher.js b/src/SizeFetcher.js index 4761281..8f4ba76 100644 --- a/src/SizeFetcher.js +++ b/src/SizeFetcher.js @@ -2,6 +2,7 @@ import React from 'react' import PropTypes from 'prop-types' import warning from './utils/warning' +import EnhanceInnerComponent from './EnhanceInnerComponent' const getDisplayName = wrappedComponent => wrappedComponent.displayName || wrappedComponent.name const isStateless = component => !component.render && !(component.prototype && component.prototype.render) @@ -30,6 +31,16 @@ const SizeFetcher = (SubComponent, options = { noComparison: false }) => { } class Enhancer extends ComposedComponent { + constructor() { + super() + + this.elementsTree = super.render() + this.enhancedChildren = this.privateEnhanceChildren(this.elementsTree.props.children) + + this.privateHandleSizeMayHaveChanged = this.privateHandleSizeMayHaveChanged.bind(this) + this.privateSizeChanged = this.privateSizeChanged.bind(this) + this.privateRegisterComponentInfos = this.privateRegisterComponentInfos.bind(this) + } componentDidMount() { if (super.componentDidMount) super.componentDidMount() const { clientHeight, clientWidth, scrollHeight, scrollWidth } = this.comp @@ -53,9 +64,9 @@ const SizeFetcher = (SubComponent, options = { noComparison: false }) => { const { sizeChange } = this.props // First call of the callback, the component mounted and we need to give its size - sizeChange({ clientHeight: clientHeight, clientWidth: clientWidth, scrollHeight: scrollHeight, scrollWidth: scrollWidth }) + sizeChange({ clientHeight, clientWidth, scrollHeight, scrollWidth }) // Register the dimension for future sake (comparison) - this.privateRegisterComponentInfos() + this.privateRegisterComponentInfos(clientHeight, clientWidth, scrollHeight, scrollWidth) } privateHandleSizeMayHaveChanged() { const { clientHeight, clientWidth, scrollHeight, scrollWidth } = this.comp @@ -66,25 +77,50 @@ const SizeFetcher = (SubComponent, options = { noComparison: false }) => { this.privateSizeChanged(clientHeight, clientWidth, scrollHeight, scrollWidth) } } - privateRegisterComponentInfos() { - const { clientHeight, clientWidth, scrollHeight, scrollWidth } = this.comp - + privateRegisterComponentInfos(clientHeight, clientWidth, scrollHeight, scrollWidth) { // Register the height & width so we can compare them in the future this.clientHeight = clientHeight this.clientWidth = clientWidth this.scrollHeight = scrollHeight this.scrollWidth = scrollWidth } + privateEnhanceChildren(child) { + let children + if (child && child.props && Object.prototype.hasOwnProperty.call(child.props, 'children')) children = child.props.children + + // Zero case: the child is multiple + if (child && Array.isArray(child)) { + return child.map(ch => this.privateEnhanceChildren(ch)) + } else if (children && Array.isArray(children)) { + // First case: the children is composed of multiple child + return Object.assign({}, child, { + props: Object.assign({}, child.props, { + children: React.Children.map(children, ch => this.privateEnhanceChildren(ch)), + }), + }) + } else if (children && children instanceof Object) { + // Second case: The children is the only one + return Object.assign({}, child, { + props: Object.assign({}, child.props, { + children: this.privateEnhanceChildren(children), + }), + }) + } else if (child && typeof child.type === 'function') { + // Third case: The children is actually an innerComponent + const EnhancedInner = EnhanceInnerComponent(child.type) + const display = this.privateHandleSizeMayHaveChanged()} /> + // console.log('-------', display, child) + return display + } + // No enhancement + return child + } render() { - // Retrieve the component render tree - const elementsTree = super.render() - // Here thanks to II, we can add a ref without the subComponent noticing - let newProps = { ref: comp => (this.comp = comp) } - + const newProps = { ref: comp => (this.comp = comp) } // Create a new component from SubComponent render with new props - const newElementsTree = React.cloneElement(elementsTree, newProps, elementsTree.props.children) + const newElementsTree = React.cloneElement(this.elementsTree, newProps, this.enhancedChildren) return newElementsTree } } diff --git a/src/index.test.js b/src/index.test.js index ad97252..b9a155f 100644 --- a/src/index.test.js +++ b/src/index.test.js @@ -25,7 +25,6 @@ describe('SizeFetcher component', () => { const initialProps = { test: 'ok' } class InnnerComponent extends React.Component { componentDidMount() { - console.log('OYOYO') this.forceUpdate() } render() { @@ -53,7 +52,7 @@ describe('SizeFetcher component', () => { } const InnerReactComponent = () => (
InnerComponent : - {InnnerComponent} +
) const EnhancedFunctionalComponent = SizeFetcher(FunctionalReactComponent, { noComparison: true }) @@ -108,6 +107,7 @@ describe('SizeFetcher component', () => { }) it('should call sizeChange function when an innerComponent update itseft', () => { const WrapperEnhancedInnerComponent = mount() + console.log(WrapperEnhancedInnerComponent.html()) // First call at mount then when sub component force its update expect(sizeChangeThird.mock.calls.length).toEqual(2) })