1 | import { EMPTY_OBJECT, merge, isObject, uuid } from '../lib/utils'
|
2 | import { Observer } from './observer'
|
3 |
|
4 | /**
|
5 | * A uuid to use as the property of the dataStore's state. This creates a pseudo-private
|
6 | */
|
7 | const dataStore = uuid()
|
8 |
|
9 | /**
|
10 | * A class to create a dataStore. This is used in conjunction with DataStoreComponent to create stateless components with external state management through a dataStore.
|
11 | */
|
12 | export class DataStore {
|
13 | constructor(props) {
|
14 | this[dataStore] = undefined
|
15 | this.observer = new Observer()
|
16 | this.state = props.state
|
17 | }
|
18 |
|
19 | /**
|
20 | * @method This is a getter to access the component's state using the pseudo-private key dataStore.
|
21 | * @return {boolean | number | string | Object | any[]} The component's state
|
22 | */
|
23 | get state() {
|
24 | return this[dataStore]
|
25 | }
|
26 |
|
27 | /**
|
28 | * @method This is a setter to define the component's state. It uses the dataStore object as a pseudo-private key. It uses requestAnimationFrame to throttle component updates to avoid layout thrashing.
|
29 | * @param {string | number | boolean | Object | any[]} data Data to set as component state.
|
30 | * @return {void} undefined
|
31 | */
|
32 | set state(data) {
|
33 | this[dataStore] = data
|
34 | this.dispatch('dataStoreStateChanged', this.state)
|
35 | }
|
36 |
|
37 | /**
|
38 | * @method This is a method to dispatch an event with data to a DataStoreComponent that is using a dataStore.
|
39 | * @param {string} event The name of the event that the component is watching.
|
40 | * @param {any} data Any data you want to send to the component.
|
41 | */
|
42 | dispatch(event, data) {
|
43 | this.observer.dispatch(event, data)
|
44 | }
|
45 |
|
46 | /**
|
47 | * @method This method sets up an observer to listener for the designated event and do something with any data passed along.
|
48 | * @param {string} event The event to watch.
|
49 | * @param {any} data Any data that the event callback will need to handle.
|
50 | */
|
51 | watch(event, data) {
|
52 | this.observer.watch(event, data)
|
53 | }
|
54 |
|
55 | /**
|
56 | * @method Method to set a dataStore's state. This accepts simple types or Objects. If updating an array, you can pass in the data and the position (number) in the array to update. Optionally you can pass a callback, which receives the state as its argument. You need to return the state changes in order for the component to be updated.
|
57 | * @example Set state on a dataStore:
|
58 | *
|
59 | * ```
|
60 | * this.setState(true)
|
61 | * this.setState(0)
|
62 | * this.setState({name: 'Joe'})
|
63 | * this.setState([1,2,3])
|
64 | * this.setState(prevState => prevState + 1)
|
65 | ```
|
66 | * @param {string | number | boolean | Object | any[] | Function} data The data to set. If a callback is passed as the argument to execute, it gets passed the previous state as its argument. You need to make sure the callback returns the final state or the component will not update.
|
67 | * @return {void} undefined
|
68 | */
|
69 | setState(data) {
|
70 | if (typeof data === 'function') {
|
71 | let copyOfState
|
72 | copyOfState = merge(EMPTY_OBJECT, this.state)
|
73 | const newState = data.call(this, copyOfState)
|
74 | if (newState) this.state = newState
|
75 | } else if (isObject(this.state) && isObject(data)) {
|
76 | const newState = merge(this.state, data)
|
77 | this.state = newState
|
78 | }
|
79 | }
|
80 | }
|