UNPKG

8.6 kBMarkdownView Raw
1Composi
2=======
3
4Contents
5--------
6- [Installation](../README.md)
7- [JSX](./jsx.md)
8- [Hyperx](./hyperx.md)
9- [Hyperscript](./hyperscript.md)
10- [Functional Components](./functional-components.md)
11- [Mount, Render and Unmount](./render.md)
12- [Components](./components.md)
13- [State](./state.md)
14- [Lifecycle Methods](./lifecycle.md)
15- [Events](./events.md)
16- [Styles](./styles.md)
17- [Unmount](./unmount.md)
18- State Management with DataStore
19 - [State Management with DataStore](#State-Management-with-DataStore)
20 - [Importing in Your Project](#Importing-in-Your-Project)
21 - [DataStore](#DataStore)
22 - [DataStoreComponent](#DataStoreComponent)
23 - [Observer](#Observer)
24 - [Watch Method](#Watch-Method)
25 - [Dispatch Method](#Dispatch-Method)
26 - [uuid](#uuid)
27- [Third Party Libraries](./third-party.md)
28- [Deployment](./deployment.md)
29- [Differences with React](./composi-react.md)
30
31## State Management with DataStore
32
33Composi's class components are a powerful way to structure your apps. Stateful class components let you create components with local state. In many situations this is a perfect way to handle state. But you may prefer to have state separate from your class components.
34
35For those situations Composi provides `DataStore` and `DataStoreComponent`. Together they create a combination similar to how Mobx works with React components. You create a `dataStore` and pass it to a `dataStore component`. When you update the data in the `dataStore`, the `dataStore component` udpates automatically.
36
37If you are going to use `DataStore` then you are also going to use `DataStoreComponent`.
38
39## Importing in Your Project
40
41Importing `DataStore` into your project is simple. You import it from Composi's `data-store` folder:
42
43```javascript
44import { DataStore, DataStoreComponent } from 'composi/data-store'
45```
46
47## DataStore
48
49After importing `DataStore`, you can create a `dataStore`. You do this using the `new` keyword and passing in the intial data to use. `DataStore` expects data in the following format:
50
51```javascript
52import { DataStore, DataStoreComponent } from 'composi/data-store'
53
54const dataStore = new DataStore({
55 state: {
56 title: 'The Current Title',
57 items: [
58 'Apples',
59 'Oranges',
60 'Bananas'
61 ]
62 }
63})
64```
65
66### setState
67
68`DataStore` has only one public method: `setState`. You use this to update the state of a `dataStore`. You do this by using a callback. This gets pass the previous state of the `dataStore`. After doing whatever you need to do with `prevState`, you need to return it. Otherwise, the `dataStore` with never dispatch its update event:
69
70```javascript
71dataStore.setState(prevState => {
72 prevState.items.push('Watermelon')
73 // Don't forget to return prevState:
74 return prevState
75})
76```
77
78## DataStoreComponent
79
80To make your `dataStore` useful, you need to pass it to an instance of `DataStoreComponent`. This is just a custom version of Composi's `Component` class witout state. It's configured to use a `dataStore` instead of state. It watches the `dataStore`. When you update the `dataStore`, it dispatches and event that `DataStoreComponent` listens for. When that happens, the component updates with the data that was sent.
81
82To create a new `DataStoreComponent` you need to extend it, just like you would with the `Component` class:
83
84```javascript
85import { h } from 'composi'
86import { DataStore, DataStoreComponent } from 'composi/data-store'
87
88// Define a dataStore:
89const dataStore = new DataStore({
90 state: {
91 title: 'The Current Title',
92 items: [
93 'Apples',
94 'Oranges',
95 'Bananas'
96 ]
97 }
98})
99
100// Define a custom component:
101class List extends DataStoreComponent {
102 render(data) {
103 return (
104 <ul class='list'>
105 {
106 data.items.map(item => <li>{item}</li>)
107 }
108 </ul>
109 )
110 }
111}
112```
113
114With the custom component defined, we can instatiate it. When doing so, we need to give provide container to render in and pass in the `dataStore`:
115
116```javascript
117const list = new List(
118 container: 'section',
119 dataStore
120)
121```
122
123And 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.
124
125Although 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.
126
127
128## Example
129
130Here's a complete example using `DataStore` and `DataStoreComponent`. In this example we separate out the code that updates the `dataStore` into an `actions` object. The component's user interactions will invoke those `actions` methods, which will result in the component itself being updated.
131
132```javascript
133import { h } from 'composi'
134import { DataStore, DataStoreComponent } from 'composi/data-store'
135
136// Define the dataStore:
137const dataStore = new DataStore({
138 state: {
139 message: 'Bozo the clown',
140 items: [
141 {
142 id: 101,
143 value: 'Apples'
144 },
145 {
146 id: 102,
147 value: 'Oranges'
148 }
149 ]
150 }
151})
152
153// Define actions to manipulate the dataStore:
154const actions = {
155 addItem(dataStore, data) {
156 dataStore.setState(prevState => {
157 prevState.items.push({
158 id: data.id,
159 value: data.value
160 })
161 return prevState
162 })
163 },
164 deleteItem(dataStore, id) {
165 dataStore.setState(prevState => {
166 prevState.items = prevState.items.filter(item => item.id != id)
167 return prevState
168 })
169 }
170}
171
172// Create a custom component by extending DataStoreComponent:
173class List extends DataStoreComponent {
174 key = 103
175 render(data) {
176 return (
177 <div class='list-container'>
178 <h2>{data.message}</h2>
179 <p>
180 <input type="text"/>
181 <button className="add-item" onclick={() => this.addItem()}>Add</button>
182 </p>
183 <ul>
184 {
185 data.items.map(item => (
186 <li key={item.id}>
187 <span>{item.value}</span>
188 <button className="delete-item" onclick={() => this.deleteItem(item.id)}>X</button>
189 </li>)
190 )
191 }
192 </ul>
193 </div>
194 )
195 }
196 componentDidMount() {
197 this.input = this.element.querySelector('input')
198 this.input.focus()
199 }
200 addItem() {
201 const value = this.input.value
202 if (value) {
203 actions.addItem(this.dataStore, {id: this.key++, value})
204 this.input.value = ''
205 this.input.focus()
206 } else {
207 alert('Please provide a value before submitting.')
208 }
209 }
210 deleteItem(id) {
211 actions.deleteItem(this.dataStore, id)
212 }
213}
214
215// Create an instance of the custom component.
216// Assign it a container and pass in the dataStore:
217const list = new List({
218 container: 'section',
219 dataStore
220})
221
222// Don't forget to pass the dataStore to the list instance through the update method.
223// You need to do this to forceß the component to mount the first time..
224list.update(dataStore.state)
225```
226
227## Observer
228
229`DataStore` exposes the `Observer` class that it uses internally so you can use it in your projects. `Observer` exposes two methods: `watch` and `dispatch`. You can use these to create an event bus to decouple your code. Like `DataStore`, you import `Observer` from Composi's `data-store` folder:
230
231```javascript
232import { Observer } from 'composi/data-store'
233```
234
235To you `Observer` you need to create a new instance first:
236
237```javascript
238import { Observer } from 'composi/data-store'
239
240const observer = new Observer()
241```
242
243### Watch Method
244
245Then you can create a watcher to listen for an event. You also need to provide a callback to execute when the event is dispatched. The callback will get passed the data as its argument:
246
247```javascript
248// Setting up a simple data.
249// We're not really doing anything with the data.
250// Just outputting it to show that it arrived.
251observer.watch('boring', function(data) {
252 console.log(`The boring event fired. Here's the data:`)
253 console.log(data)
254})
255```
256
257### Dispatch Method
258
259Then we can dispatch our event with some data:
260
261```javascript
262observer.dispatch('boring', 'This is the new data.')
263```
264
265You can pass whatever kind of data you need to: boolean, number, string, array or object.
266
267## uuid
268
269The DataStore class uses the `uuid` function to create an RFC4122 version 4 compliant uuid. This is a random string of 36 characters. This is used internally by DataStore. You can also import `uuid` into your project to create a uuid for your own code.
270
271```javascript
272import { uuid } from 'composi/data-store
273
274const id = uuid()
275```