Skip to content
This repository has been archived by the owner on Sep 3, 2022. It is now read-only.

Commit

Permalink
DataStore Component mounts automatically.
Browse files Browse the repository at this point in the history
1. DataStore components now mount automatically, using the state of their dataStore.
2. Update DataStore Component test for new mounting functionality.
3. Update DataStore test  to check for automatic mounting.
4. Update merge to accept multiple objects. If a single object is passed int, it clones it. Merge now does a deep clone of objects.
5. Added a new utility function: getType. This returns the type of primitive and object types as a capitalized value: Null, Undefined, String, Number, NaN, Function, Array, Object, RegExp, Symbol, etc.
6. Renamed isSameVNode to areEqual for more generic use for value comparison.
7. Updated Component class to use getType and isSameObject.
8. Added unwatch to Observer class. Pass in the event to unwatch. This cancels all watchers for that event.
9. Updated observer test for unwatch.
Wobbabits committed Sep 15, 2018
1 parent 66d7f62 commit 01f8b75
Showing 13 changed files with 79 additions and 5,866 deletions.
1 change: 1 addition & 0 deletions data-store/dataStore-component.js
Original file line number Diff line number Diff line change
@@ -15,6 +15,7 @@ export class DataStoreComponent extends Component {
this.dataStore = /** @type {DataStore}*/(props.dataStore)
if (props.dataStore instanceof DataStore) {
props.dataStore.watch('dataStoreStateChanged', () => this.update(this.dataStore.state))
this.update(this.dataStore.state)
}
}
}
4 changes: 2 additions & 2 deletions data-store/dataStore.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { EMPTY_OBJECT, merge, isObject, uuid } from '../lib/utils'
import { EMPTY_OBJECT, merge, getType, uuid } from '../lib/utils'
import { Observer } from './observer'

/**
@@ -72,7 +72,7 @@ export class DataStore {
copyOfState = merge(EMPTY_OBJECT, this.state)
const newState = data.call(this, copyOfState)
if (newState) this.state = newState
} else if (isObject(this.state) && isObject(data)) {
} else if (getType(this.state) === 'Object' && getType(data) === 'Object') {
const newState = merge(this.state, data)
this.state = newState
}
9 changes: 9 additions & 0 deletions data-store/observer.js
Original file line number Diff line number Diff line change
@@ -35,4 +35,13 @@ export class Observer {
}
return this.events[event].map(callback => callback(data));
}

/**
* Remove an event from cache.
* @param {string} event
* @return {void} undefined
*/
unwatch(event) {
delete this.events[event]
}
}
2 changes: 1 addition & 1 deletion dist/composi.js

Large diffs are not rendered by default.

Binary file modified dist/composi.js.gz
Binary file not shown.
2 changes: 1 addition & 1 deletion dist/composi.js.map

Large diffs are not rendered by default.

6 changes: 1 addition & 5 deletions docs/data-store.md
Original file line number Diff line number Diff line change
@@ -122,12 +122,8 @@ const list = new List(

And that's it. The component is now linked to the `dataStore`. If we change the `dataStore's` state, the component will udpate automatically. We would do that using `setState` on the `dataStore`, as we did above.

Because the component has no local state, as it currently stands the component hasn't mount. As it is, it won't mount until the `dataStore` gets updated. In order to mount the component the first time, we need to invoke its `update` method and pass in the `dataStore` state:
Although the component has no local state, the component will mount as soon as it's instantiated. This is new in version 3.2.0. In earlier versions you had to mount the component using the `update` method.

```javascript
list.update(dataStore.state)
```
This will mount the component for us.

## Example

14 changes: 4 additions & 10 deletions lib/component.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,4 @@
import {
isObject,
EMPTY_OBJECT,
EMPTY_ARRAY,
merge,
isSameVNode
} from './utils'
import { getType, EMPTY_OBJECT, EMPTY_ARRAY, merge, areEqual } from './utils'
import { patch } from './vdom'
import { mount } from './mount'
import { vnodeFromElement } from './vnode'
@@ -252,7 +246,7 @@ export class Component {
setState(data) {
if (typeof data === 'function') {
let copyOfState
if (isObject(this.state)) {
if (getType(this.state) === 'Object') {
copyOfState = merge(EMPTY_OBJECT, this.state)
} else if (Array.isArray(this.state)) {
copyOfState = EMPTY_ARRAY.concat(EMPTY_ARRAY, this.state)
@@ -261,7 +255,7 @@ export class Component {
}
const newState = data.call(this, copyOfState)
if (newState) this.state = newState
} else if (isObject(this.state) && isObject(data)) {
} else if (getType(this.state) === 'Object' && getType(data) === 'Object') {
const newState = merge(this.state, data)
this.state = newState
} else {
@@ -347,7 +341,7 @@ export class Component {
return
} else {
// The vnodes are identical, so exit:
if (isSameVNode(vdom, this.oldVNode)) {
if (areEqual(vdom, this.oldVNode)) {
return
} else {
// Update mounted component:
51 changes: 26 additions & 25 deletions lib/utils.js
Original file line number Diff line number Diff line change
@@ -42,39 +42,26 @@ export const EMPTY_ARRAY = []

/**
* Combine two objects, merging the second into the first. Any properties already existing in the first will be replaced by those of the second. Any properties in the second not in the first will be added to it.
* @param {Object.<string, any>} firstObject
* @param {Object.<string, any>} secondObject
* @return {Object.<string, any>} target
* @param {Object.<string, any>[]} objects
* @return {Object.<string, any>} Object.<string, any>
*/
export function merge(firstObject, secondObject) {
const target = {}

for (let i in firstObject) target[i] = firstObject[i]
for (let j in secondObject) target[j] = secondObject[j]

return target
}

/**
* A function to test where something is an object literal or not. Used by Component setState.
* @param {Object.<string, any>} obj An object literal to test.
* @return {boolean} boolean
*/
export function isObject(obj) {
if (Array.isArray(obj)) return false
else if (typeof obj === 'object') return true
return false
export function merge(...objects) {
// Clone both objects:
const clones = objects.map(obj => JSON.parse(JSON.stringify(obj)))
// Merge objects:
return clones.reduce((a, b) => Object.assign({}, a, b))
}

/**
* A function to test whether the data provided for updating a component creates a new virtual node or not.
* @typedef {import('./vnode').VNode} VNode
* @param {VNode} oldVNode The previous virtual node of a component.
* @param {VNode} newVNode The current virtual node of a component.
* @param {Object<string, any> | VNode} value1 The previous virtual node of a component.
* @param {Object<string, any> | VNode} value2 The current virtual node of a component.
* @return {boolean} boolean
*/
export function isSameVNode(oldVNode, newVNode) {
return JSON.stringify(oldVNode) === JSON.stringify(newVNode)
export function areEqual(value1, value2) {
if (getType(value1) !== 'Object' && getType(value2) !== 'Object') return
return JSON.stringify(value1) === JSON.stringify(value2)
}

/**
@@ -108,3 +95,17 @@ export function uuid() {
return (c === 'x' ? r : (r & 0x3) | 0x8).toString(16)
})
}

/**
* Determine the type of the provided value.
* @param {*} value
* @return {string} string
*/
export function getType(value) {
// Capture NaN values:
if (typeof value === 'number' && isNaN(value)) {
return 'NaN'
} else {
return new RegExp('\\[object (.*)]').exec(toString.call(value))[1]
}
}
20 changes: 13 additions & 7 deletions lib/vdom.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
import { RECYCLED_NODE, TEXT_NODE, XLINK_NS, SVG_NS, merge } from './utils'
import {
RECYCLED_NODE,
TEXT_NODE,
XLINK_NS,
SVG_NS,
merge,
getType
} from './utils'

/**
* Event proxy for inline events.
@@ -51,11 +58,8 @@ function createKeyMap(children, start, end) {
* @return {void} undefined
*/
function setProp(element, prop, oldValue, newValue, isSVG) {
if (
prop === 'style' &&
typeof newValue === 'object' &&
!Array.isArray(newValue)
) {
if (oldValue === newValue) return
if (prop === 'style' && getType(newValue) === 'Object') {
for (let i in merge(oldValue, newValue)) {
const style = newValue == null || newValue[i] == null ? '' : newValue[i]
if (i[0] === '-') {
@@ -69,7 +73,9 @@ function setProp(element, prop, oldValue, newValue, isSVG) {

if (prop[0] === 'o' && prop[1] === 'n') {
if (!element['events']) element['events'] = {}
element['events'][(prop = prop.slice(2).toLowerCase())] = newValue
prop = prop.slice(2).toLowerCase()
if (!oldValue) oldValue = element['events'][prop]
element['events'][prop] = newValue

if (newValue == null) {
element.removeEventListener(prop, eventProxy)
Loading

0 comments on commit 01f8b75

Please sign in to comment.