Compare commits
1 Commits
server-bas
...
computed-d
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
595c4bd5a8 |
126
src/common/computed.js
Normal file
126
src/common/computed.js
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
import React, { PureComponent } from 'react'
|
||||||
|
|
||||||
|
const {
|
||||||
|
create,
|
||||||
|
defineProperty,
|
||||||
|
defineProperties,
|
||||||
|
getOwnPropertyDescriptors = obj => {
|
||||||
|
const descriptors = {}
|
||||||
|
const { getOwnPropertyDescriptor } = Object
|
||||||
|
for (const prop in obj) {
|
||||||
|
const descriptor = getOwnPropertyDescriptor(obj, prop)
|
||||||
|
if (descriptor !== undefined) {
|
||||||
|
descriptors[prop] = descriptor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return descriptors
|
||||||
|
},
|
||||||
|
prototype: { hasOwnProperty },
|
||||||
|
} = Object
|
||||||
|
|
||||||
|
const makePropsSpy =
|
||||||
|
typeof Proxy !== 'undefined'
|
||||||
|
? (obj, spy) =>
|
||||||
|
new Proxy(obj, {
|
||||||
|
get: (target, property) => (spy[property] = target[property]),
|
||||||
|
})
|
||||||
|
: (obj, spy) => {
|
||||||
|
const descriptors = {}
|
||||||
|
const props = getOwnPropertyDescriptors(obj)
|
||||||
|
for (const prop in props) {
|
||||||
|
const { configurable, enumerable, get, value } = props[prop]
|
||||||
|
descriptors[prop] = {
|
||||||
|
configurable,
|
||||||
|
enumerable,
|
||||||
|
get:
|
||||||
|
get !== undefined
|
||||||
|
? () => (spy[prop] = get.call(obj))
|
||||||
|
: () => (spy[prop] = value),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return create(null, descriptors)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decorator which provides computed properties for React components.
|
||||||
|
//
|
||||||
|
// ```js
|
||||||
|
// const MyComponent = computed({
|
||||||
|
// fullName: ({ firstName, lastName }) => `${lastName}, ${firstName}`
|
||||||
|
// })(({ fullName }) =>
|
||||||
|
// <p>{fullName}</p>
|
||||||
|
// )
|
||||||
|
// ```
|
||||||
|
const computed = computed => Component =>
|
||||||
|
class extends PureComponent {
|
||||||
|
constructor () {
|
||||||
|
super()
|
||||||
|
|
||||||
|
this._computedCache = create(null)
|
||||||
|
this._computedDeps = create(null)
|
||||||
|
|
||||||
|
const descriptors = (this._descriptors = {})
|
||||||
|
for (const name in computed) {
|
||||||
|
if (!hasOwnProperty.call(computed, name)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
const transform = computed[name]
|
||||||
|
let running = false
|
||||||
|
descriptors[name] = {
|
||||||
|
configurable: true,
|
||||||
|
enumerable: true,
|
||||||
|
get: () => {
|
||||||
|
// this is necessary to allow a computed value to depend on
|
||||||
|
// itself
|
||||||
|
if (running) {
|
||||||
|
console.log(name, 'running')
|
||||||
|
return this.props[name]
|
||||||
|
}
|
||||||
|
|
||||||
|
const cache = this._computedCache
|
||||||
|
const deps = this._computedDeps
|
||||||
|
|
||||||
|
const dependencies = deps[name]
|
||||||
|
let needsRecompute = dependencies === undefined
|
||||||
|
if (!needsRecompute) {
|
||||||
|
const { props } = this
|
||||||
|
for (const depName in dependencies) {
|
||||||
|
const value =
|
||||||
|
depName === name || !(depName in cache)
|
||||||
|
? props[depName]
|
||||||
|
: cache[depName]
|
||||||
|
needsRecompute = value !== dependencies[depName]
|
||||||
|
if (needsRecompute) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(name, needsRecompute)
|
||||||
|
|
||||||
|
if (needsRecompute) {
|
||||||
|
running = true
|
||||||
|
cache[name] = transform(
|
||||||
|
makePropsSpy(this._props, (deps[name] = create(null)))
|
||||||
|
)
|
||||||
|
running = false
|
||||||
|
}
|
||||||
|
|
||||||
|
const value = cache[name]
|
||||||
|
defineProperty(this._props, name, {
|
||||||
|
enumerable: true,
|
||||||
|
value,
|
||||||
|
})
|
||||||
|
return value
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render () {
|
||||||
|
this._props = defineProperties({ ...this.props }, this._descriptors)
|
||||||
|
|
||||||
|
return <Component {...this._props} />
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export { computed as default }
|
||||||
@@ -9,6 +9,7 @@ import { getUser } from 'selectors'
|
|||||||
import { serverVersion } from 'xo'
|
import { serverVersion } from 'xo'
|
||||||
import { Container, Row, Col } from 'grid'
|
import { Container, Row, Col } from 'grid'
|
||||||
import { connectStore, getXoaPlan } from 'utils'
|
import { connectStore, getXoaPlan } from 'utils'
|
||||||
|
import computed from 'computed'
|
||||||
|
|
||||||
import pkg from '../../../package'
|
import pkg from '../../../package'
|
||||||
|
|
||||||
@@ -25,6 +26,20 @@ const HEADER = (
|
|||||||
</Container>
|
</Container>
|
||||||
)
|
)
|
||||||
|
|
||||||
|
let MyComponent = _ => <p>{_.firstName}</p>
|
||||||
|
|
||||||
|
MyComponent = computed({
|
||||||
|
firstName: ({ firstName }) => firstName.toUpperCase(),
|
||||||
|
fullName: _ =>
|
||||||
|
_.firstName === 'Bob'
|
||||||
|
? 'Bobinette'
|
||||||
|
: `${_.title} ${_.lastName}, ${_.firstName}`,
|
||||||
|
})(MyComponent)
|
||||||
|
|
||||||
|
MyComponent = computed({
|
||||||
|
title: () => 'Mr',
|
||||||
|
})(MyComponent)
|
||||||
|
|
||||||
@connectStore(() => ({
|
@connectStore(() => ({
|
||||||
user: getUser,
|
user: getUser,
|
||||||
}))
|
}))
|
||||||
@@ -34,12 +49,30 @@ export default class About extends Component {
|
|||||||
this.setState({ serverVersion })
|
this.setState({ serverVersion })
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
state = {
|
||||||
|
firstName: 'John',
|
||||||
|
lastName: 'Smith',
|
||||||
|
};
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { user } = this.props
|
const { user } = this.props
|
||||||
const isAdmin = user && user.permission === 'admin'
|
const isAdmin = user && user.permission === 'admin'
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Page header={HEADER} title='aboutPage' formatTitle>
|
<Page header={HEADER} title='aboutPage' formatTitle>
|
||||||
|
<input
|
||||||
|
value={this.state.firstName}
|
||||||
|
onChange={this.linkState('firstName')}
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
value={this.state.lastName}
|
||||||
|
onChange={this.linkState('lastName')}
|
||||||
|
/>
|
||||||
|
<MyComponent
|
||||||
|
firstName={this.state.firstName}
|
||||||
|
lastName={this.state.lastName}
|
||||||
|
/>
|
||||||
<Container className='text-xs-center'>
|
<Container className='text-xs-center'>
|
||||||
{isAdmin && (
|
{isAdmin && (
|
||||||
<Row>
|
<Row>
|
||||||
|
|||||||
Reference in New Issue
Block a user