Skip to content

Commit

Permalink
fix: Proxy should keep methods own props. #918
Browse files Browse the repository at this point in the history
This includes name, arity, toString and everything else possible stored among original method.
  • Loading branch information
theKashey committed Apr 20, 2018
1 parent 1ec9ec2 commit a84dcd0
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 4 deletions.
34 changes: 30 additions & 4 deletions src/proxy/createClassProxy.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,32 @@ const setSFPFlag = (component, flag) =>
value: flag,
})

const copyMethodDescriptors = (target, source) => {
if (source) {
// it is possible to use `function-double` to construct an ideal clone, but does not make a sence
const keys = Object.getOwnPropertyNames(source)

keys.forEach(key =>
safeDefineProperty(
target,
key,
Object.getOwnPropertyDescriptor(source, key),
),
)

safeDefineProperty(target, 'toString', {
configurable: true,
writable: false,
enumerable: false,
value: function toString() {
return String(source)
},
})
}

return target
}

function createClassProxy(InitialComponent, proxyKey, options) {
const renderOptions = {
...defaultRenderOptions,
Expand Down Expand Up @@ -103,21 +129,21 @@ function createClassProxy(InitialComponent, proxyKey, options) {
}

function lifeCycleWrapperFactory(wrapperName, sideEffect = identity) {
return function wrappedMethod(...rest) {
return copyMethodDescriptors(function wrappedMethod(...rest) {
proxiedUpdate.call(this)
sideEffect(this)
return (
!isFunctionalComponent &&
CurrentComponent.prototype[wrapperName] &&
CurrentComponent.prototype[wrapperName].apply(this, rest)
)
}
}, InitialComponent.prototype && InitialComponent.prototype[wrapperName])
}

function methodWrapperFactory(wrapperName, realMethod) {
return function wrappedMethod(...rest) {
return copyMethodDescriptors(function wrappedMethod(...rest) {
return realMethod.apply(this, rest)
}
}, realMethod)
}

const fakeBasePrototype = Base =>
Expand Down
24 changes: 24 additions & 0 deletions test/AppContainer.dev.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import React, { Component } from 'react'
import createReactClass from 'create-react-class'
import { mount } from 'enzyme'
import { mapProps } from 'recompose'
import { polyfill } from 'react-lifecycles-compat'
import { AppContainer } from '../src/index.dev'
import RHL from '../src/reactHotLoader'
import { increment as incrementGeneration } from '../src/global/generation'
Expand Down Expand Up @@ -1398,6 +1399,7 @@ describe(`AppContainer (dev)`, () => {
return <div>I AM NEW CHILD</div>
}
}

RHL.register(App, 'App3', 'test.js')
wrapper.setProps({ children: <App /> })
expect(spy).not.toHaveBeenCalled()
Expand Down Expand Up @@ -1946,5 +1948,27 @@ describe(`AppContainer (dev)`, () => {

expect(wrapper.text()).toBe('new render + old state + 20')
})

it('should work with react-lifecycle-compact', () => {
class Component extends React.Component {
static getDerivedStateFromProps() {
return {}
}
}

/* eslint-disable no-underscore-dangle */
polyfill(Component)
expect(
Component.prototype.componentWillReceiveProps
.__suppressDeprecationWarning,
).toBe(true)

const Proxy = <Component />.type
expect(
Proxy.prototype.componentWillReceiveProps.__suppressDeprecationWarning,
).toBe(true)

/* eslint-enable */
})
})
})
29 changes: 29 additions & 0 deletions test/proxy/instance-method.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -138,4 +138,33 @@ describe('instance method', () => {
})
})
})

it('passes methods props thought', () => {
const injectedMethod = (a, b) => this[24 + a + b]

injectedMethod.staticProp = 'magic'

class App extends React.Component {
method() {
return 42
}
}

App.prototype.injectedMethod = injectedMethod

const app1 = new App()

expect(app1.injectedMethod).toBe(injectedMethod)
expect(app1.injectedMethod.staticProp).toBe('magic')
expect(String(app1.injectedMethod)).toBe(String(injectedMethod))

const Proxy = createProxy(App).get()

const app2 = new Proxy()

expect(app2.injectedMethod).not.toBe(injectedMethod)
expect(app2.injectedMethod.staticProp).toBe('magic')
expect(app2.injectedMethod.length).toBe(2)
expect(String(app2.injectedMethod)).toBe(String(injectedMethod))
})
})

0 comments on commit a84dcd0

Please sign in to comment.