react-native-onyx 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/API.md ADDED
@@ -0,0 +1,187 @@
1
+ <!---These docs were automatically generated. Do not edit them directly run `npm run build-docs` script-->
2
+
3
+ # API Reference
4
+
5
+ ## Functions
6
+
7
+ <dl>
8
+ <dt><a href="#connect">connect(mapping)</a> ⇒ <code>Number</code></dt>
9
+ <dd><p>Subscribes a react component&#39;s state directly to a store key</p>
10
+ </dd>
11
+ <dt><a href="#disconnect">disconnect(connectionID, [keyToRemoveFromEvictionBlocklist])</a></dt>
12
+ <dd><p>Remove the listener for a react component</p>
13
+ </dd>
14
+ <dt><a href="#set">set(key, value)</a> ⇒ <code>Promise</code></dt>
15
+ <dd><p>Write a value to our store with the given key</p>
16
+ </dd>
17
+ <dt><a href="#multiSet">multiSet(data)</a> ⇒ <code>Promise</code></dt>
18
+ <dd><p>Sets multiple keys and values</p>
19
+ </dd>
20
+ <dt><a href="#merge">merge(key, value)</a> ⇒ <code>Promise</code></dt>
21
+ <dd><p>Merge a new value into an existing value at a key.</p>
22
+ <p>The types of values that can be merged are <code>Object</code> and <code>Array</code>. To set another type of value use <code>Onyx.set()</code>. Merge
23
+ behavior uses lodash/merge under the hood for <code>Object</code> and simple concatenation for <code>Array</code>. However, it&#39;s important
24
+ to note that if you have an array value property on an <code>Object</code> that the default behavior of lodash/merge is not to
25
+ concatenate. See here: <a href="https://github.com/lodash/lodash/issues/2872">https://github.com/lodash/lodash/issues/2872</a></p>
26
+ <p>Calls to <code>Onyx.merge()</code> are batched so that any calls performed in a single tick will stack in a queue and get
27
+ applied in the order they were called. Note: <code>Onyx.set()</code> calls do not work this way so use caution when mixing
28
+ <code>Onyx.merge()</code> and <code>Onyx.set()</code>.</p>
29
+ </dd>
30
+ <dt><a href="#clear">clear()</a> ⇒ <code>Promise.&lt;void&gt;</code></dt>
31
+ <dd><p>Clear out all the data in the store</p>
32
+ </dd>
33
+ <dt><a href="#mergeCollection">mergeCollection(collectionKey, collection)</a> ⇒ <code>Promise</code></dt>
34
+ <dd><p>Merges a collection based on their keys</p>
35
+ </dd>
36
+ <dt><a href="#init">init([options])</a></dt>
37
+ <dd><p>Initialize the store with actions and listening for storage events</p>
38
+ </dd>
39
+ </dl>
40
+
41
+ <a name="connect"></a>
42
+
43
+ ## connect(mapping) ⇒ <code>Number</code>
44
+ Subscribes a react component's state directly to a store key
45
+
46
+ **Kind**: global function
47
+ **Returns**: <code>Number</code> - an ID to use when calling disconnect
48
+
49
+ | Param | Type | Description |
50
+ | --- | --- | --- |
51
+ | mapping | <code>Object</code> | the mapping information to connect Onyx to the components state |
52
+ | mapping.key | <code>String</code> | ONYXKEY to subscribe to |
53
+ | [mapping.statePropertyName] | <code>String</code> | the name of the property in the state to connect the data to |
54
+ | [mapping.withOnyxInstance] | <code>Object</code> | whose setState() method will be called with any changed data This is used by React components to connect to Onyx |
55
+ | [mapping.callback] | <code>function</code> | a method that will be called with changed data This is used by any non-React code to connect to Onyx |
56
+ | [mapping.initWithStoredValues] | <code>Boolean</code> | If set to false, then no data will be prefilled into the component |
57
+
58
+ **Example**
59
+ ```js
60
+ const connectionID = Onyx.connect({
61
+ key: ONYXKEYS.SESSION,
62
+ callback: onSessionChange,
63
+ });
64
+ ```
65
+ <a name="disconnect"></a>
66
+
67
+ ## disconnect(connectionID, [keyToRemoveFromEvictionBlocklist])
68
+ Remove the listener for a react component
69
+
70
+ **Kind**: global function
71
+
72
+ | Param | Type | Description |
73
+ | --- | --- | --- |
74
+ | connectionID | <code>Number</code> | unique id returned by call to Onyx.connect() |
75
+ | [keyToRemoveFromEvictionBlocklist] | <code>String</code> | |
76
+
77
+ **Example**
78
+ ```js
79
+ Onyx.disconnect(connectionID);
80
+ ```
81
+ <a name="set"></a>
82
+
83
+ ## set(key, value) ⇒ <code>Promise</code>
84
+ Write a value to our store with the given key
85
+
86
+ **Kind**: global function
87
+
88
+ | Param | Type | Description |
89
+ | --- | --- | --- |
90
+ | key | <code>String</code> | ONYXKEY to set |
91
+ | value | <code>\*</code> | value to store |
92
+
93
+ <a name="multiSet"></a>
94
+
95
+ ## multiSet(data) ⇒ <code>Promise</code>
96
+ Sets multiple keys and values
97
+
98
+ **Kind**: global function
99
+
100
+ | Param | Type | Description |
101
+ | --- | --- | --- |
102
+ | data | <code>Object</code> | object keyed by ONYXKEYS and the values to set |
103
+
104
+ **Example**
105
+ ```js
106
+ Onyx.multiSet({'key1': 'a', 'key2': 'b'});
107
+ ```
108
+ <a name="merge"></a>
109
+
110
+ ## merge(key, value) ⇒ <code>Promise</code>
111
+ Merge a new value into an existing value at a key.
112
+
113
+ The types of values that can be merged are `Object` and `Array`. To set another type of value use `Onyx.set()`. Merge
114
+ behavior uses lodash/merge under the hood for `Object` and simple concatenation for `Array`. However, it's important
115
+ to note that if you have an array value property on an `Object` that the default behavior of lodash/merge is not to
116
+ concatenate. See here: https://github.com/lodash/lodash/issues/2872
117
+
118
+ Calls to `Onyx.merge()` are batched so that any calls performed in a single tick will stack in a queue and get
119
+ applied in the order they were called. Note: `Onyx.set()` calls do not work this way so use caution when mixing
120
+ `Onyx.merge()` and `Onyx.set()`.
121
+
122
+ **Kind**: global function
123
+
124
+ | Param | Type | Description |
125
+ | --- | --- | --- |
126
+ | key | <code>String</code> | ONYXKEYS key |
127
+ | value | <code>Object</code> \| <code>Array</code> | Object or Array value to merge |
128
+
129
+ **Example**
130
+ ```js
131
+ Onyx.merge(ONYXKEYS.EMPLOYEE_LIST, ['Joe']); // -> ['Joe']
132
+ Onyx.merge(ONYXKEYS.EMPLOYEE_LIST, ['Jack']); // -> ['Joe', 'Jack']
133
+ Onyx.merge(ONYXKEYS.POLICY, {id: 1}); // -> {id: 1}
134
+ Onyx.merge(ONYXKEYS.POLICY, {name: 'My Workspace'}); // -> {id: 1, name: 'My Workspace'}
135
+ ```
136
+ <a name="clear"></a>
137
+
138
+ ## clear() ⇒ <code>Promise.&lt;void&gt;</code>
139
+ Clear out all the data in the store
140
+
141
+ **Kind**: global function
142
+ <a name="mergeCollection"></a>
143
+
144
+ ## mergeCollection(collectionKey, collection) ⇒ <code>Promise</code>
145
+ Merges a collection based on their keys
146
+
147
+ **Kind**: global function
148
+
149
+ | Param | Type | Description |
150
+ | --- | --- | --- |
151
+ | collectionKey | <code>String</code> | e.g. `ONYXKEYS.COLLECTION.REPORT` |
152
+ | collection | <code>Object</code> | Object collection keyed by individual collection member keys and values |
153
+
154
+ **Example**
155
+ ```js
156
+ Onyx.mergeCollection(ONYXKEYS.COLLECTION.REPORT, {
157
+ [`${ONYXKEYS.COLLECTION.REPORT}1`]: report1,
158
+ [`${ONYXKEYS.COLLECTION.REPORT}2`]: report2,
159
+ });
160
+ ```
161
+ <a name="init"></a>
162
+
163
+ ## init([options])
164
+ Initialize the store with actions and listening for storage events
165
+
166
+ **Kind**: global function
167
+
168
+ | Param | Type | Default | Description |
169
+ | --- | --- | --- | --- |
170
+ | [options] | <code>Object</code> | <code>{}</code> | config object |
171
+ | [options.keys] | <code>Object</code> | <code>{}</code> | `ONYXKEYS` constants object |
172
+ | [options.initialKeyStates] | <code>Object</code> | <code>{}</code> | initial data to set when `init()` and `clear()` is called |
173
+ | [options.safeEvictionKeys] | <code>Array.&lt;String&gt;</code> | <code>[]</code> | This is an array of keys (individual or collection patterns) that when provided to Onyx are flagged as "safe" for removal. Any components subscribing to these keys must also implement a canEvict option. See the README for more info. |
174
+ | [options.maxCachedKeysCount] | <code>Number</code> | <code>55</code> | Sets how many recent keys should we try to keep in cache Setting this to 0 would practically mean no cache We try to free cache when we connect to a safe eviction key |
175
+ | [options.captureMetrics] | <code>Boolean</code> | | Enables Onyx benchmarking and exposes the get/print/reset functions |
176
+ | [options.shouldSyncMultipleInstances] | <code>Boolean</code> | | Auto synchronize storage events between multiple instances of Onyx running in different tabs/windows. Defaults to true for platforms that support local storage (web/desktop) |
177
+ | [option.keysToDisableSyncEvents] | <code>Array.&lt;String&gt;</code> | <code>[]</code> | Contains keys for which we want to disable sync event across tabs. |
178
+
179
+ **Example**
180
+ ```js
181
+ Onyx.init({
182
+ keys: ONYXKEYS,
183
+ initialKeyStates: {
184
+ [ONYXKEYS.SESSION]: {loading: false},
185
+ },
186
+ });
187
+ ```
package/LICENSE.md ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2020 Expensify Inc.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,258 @@
1
+ # `react-native-onyx`
2
+ Persistent storage solution wrapped in a Pub/Sub library.
3
+
4
+ # Features
5
+
6
+ - Onyx stores and retrieves data from persistent storage
7
+ - Data is stored as key/value pairs, where the value can be anything from a single piece of data to a complex object
8
+ - Collections of data are usually not stored as a single key (e.g. an array with multiple objects), but as individual keys+ID (e.g. `report_1234`, `report_4567`, etc.). Store collections as individual keys when a component will bind directly to one of those keys. For example: reports are stored as individual keys because `SidebarLink.js` binds to the individual report keys for each link. However, report actions are stored as an array of objects because nothing binds directly to a single report action.
9
+ - Onyx allows other code to subscribe to changes in data, and then publishes change events whenever data is changed
10
+ - Anything needing to read Onyx data needs to:
11
+ 1. Know what key the data is stored in (for web, you can find this by looking in the JS console > Application > local storage)
12
+ 2. Subscribe to changes of the data for a particular key or set of keys. React components use `withOnyx()` and non-React libs use `Onyx.connect()`.
13
+ 3. Get initialized with the current value of that key from persistent storage (Onyx does this by calling `setState()` or triggering the `callback` with the values currently on disk as part of the connection process)
14
+ - Subscribing to Onyx keys is done using a constant defined in `ONYXKEYS`. Each Onyx key represents either a collection of items or a specific entry in storage. For example, since all reports are stored as individual keys like `report_1234`, if code needs to know about all the reports (e.g. display a list of them in the nav menu), then it would subscribe to the key `ONYXKEYS.COLLECTION.REPORT`.
15
+
16
+ # Getting Started
17
+
18
+ ## Installation
19
+
20
+ At the moment, Onyx is not yet published to `npm`. To use in your project, reference the latest sha of the main branch directly in `package.json`
21
+
22
+ ```json
23
+ "dependencies": {
24
+ "react-native-onyx": "git+https://github.com/Expensify/react-native-onyx.git#ccb64c738b8bbe933b8997eb177f864e5139bd8d"
25
+ }
26
+ ```
27
+
28
+ ## Initialization
29
+
30
+ To initialize Onyx we call `Onyx.init()` with a configuration object.
31
+
32
+ ```javascript
33
+ import Onyx from 'react-native-onyx';
34
+
35
+ const ONYXKEYS = {
36
+ SESSION: 'session',
37
+ };
38
+
39
+ const config = {
40
+ keys: ONYXKEYS,
41
+ };
42
+
43
+ Onyx.init(config);
44
+ ```
45
+
46
+ ### Usage in non react-native projects
47
+ Onyx can be used in non react-native projects, by leveraging the `browser` field in `package.json`
48
+ Bundlers like Webpack respect that field and import code from the specified path
49
+ We import Onyx the same way shown above - `import Onyx from 'react-native-onyx'`
50
+
51
+ ## Setting data
52
+
53
+ To store some data we can use the `Onyx.set()` method.
54
+
55
+ ```javascript
56
+ API.Authenticate(params)
57
+ .then((response) => {
58
+ Onyx.set(ONYXKEYS.SESSION, {token: response.token});
59
+ });
60
+ ```
61
+
62
+ The data will then be cached and stored via [`AsyncStorage`](https://github.com/react-native-async-storage/async-storage).
63
+
64
+ ## Merging data
65
+
66
+ We can also use `Onyx.merge()` to merge new `Object` or `Array` data in with existing data.
67
+
68
+ For `Array` the default behavior is to concatenate new items.
69
+
70
+ ```javascript
71
+ Onyx.merge(ONYXKEYS.EMPLOYEE_LIST, ['Joe']); // -> ['Joe']
72
+ Onyx.merge(ONYXKEYS.EMPLOYEE_LIST, ['Jack']); // -> ['Joe', 'Jack']
73
+ ```
74
+
75
+ For `Object` values the default behavior uses `lodash/merge` under the hood to do a deep extend of the object.
76
+
77
+ ```javascript
78
+ Onyx.merge(ONYXKEYS.POLICY, {id: 1}); // -> {id: 1}
79
+ Onyx.merge(ONYXKEYS.POLICY, {name: 'My Workspace'}); // -> {id: 1, name: 'My Workspace'}
80
+ ```
81
+
82
+ One caveat to be aware of is that `lodash/merge` [follows the behavior of jQuery's deep extend](https://github.com/lodash/lodash/issues/2872) and will not concatenate nested arrays in objects. It might seem like this code would concat these arrays, but it does not.
83
+
84
+ ```javascript
85
+ Onyx.merge(ONYXKEYS.POLICY, {employeeList: ['Joe']}); // -> {employeeList: ['Joe']}
86
+ Onyx.merge(ONYXKEYS.POLICY, {employeeList: ['Jack']}); // -> {employeeList: ['Jack']}
87
+ ```
88
+
89
+ ### Should I use `merge()` or `set()` or both?
90
+
91
+ - Use `merge()` if we want to merge partial data into an existing `Array` or `Object`
92
+ - Use `set()` if we are working with simple values (`String`, `Boolean`, etc), need to completely overwrite a complex property of an `Object`, or reset some data entirely.
93
+
94
+ Consecutive calls to `Onyx.merge()` with the same key are batched in a stack and processed in the order that they were called. This helps avoid race conditions where one merge possibly finishes before another. However, it's important to note that calls to `Onyx.set()` are not batched together with calls to `Onyx.merge()`. For this reason, it is usually preferable to use one or the other, but not both. Onyx is a work-in-progress so always test code to make sure assumptions are correct!
95
+
96
+ ## Subscribing to data changes
97
+
98
+ To set up a basic subscription for a given key use the `Onyx.connect()` method.
99
+
100
+ ```javascript
101
+ let session;
102
+ const connectionID = Onyx.connect({
103
+ key: ONYXKEYS.SESSION,
104
+ callback: (val) => session = val || {},
105
+ });
106
+ ```
107
+
108
+ To teardown the subscription call `Onyx.disconnect()` with the `connectionID` returned from `Onyx.connect()`. It's recommended to clean up subscriptions anytime you are connecting from within a function to prevent memory leaks.
109
+
110
+ ```javascript
111
+ Onyx.disconnect(connectionID);
112
+ ```
113
+
114
+ We can also access values inside React components via the `withOnyx()` [higher order component](https://reactjs.org/docs/higher-order-components.html). When the data changes the component will re-render.
115
+
116
+ ```javascript
117
+ import React from 'react';
118
+ import {withOnyx} from 'react-native-onyx';
119
+
120
+ const App = ({session}) => (
121
+ <View>
122
+ {session.token ? <Text>Logged in</Text> : <Text>Logged out</Text> }
123
+ </View>
124
+ );
125
+
126
+ export default withOnyx({
127
+ session: {
128
+ key: ONYXKEYS.SESSION,
129
+ },
130
+ })(App);
131
+ ```
132
+
133
+ It is preferable to use the HOC over `Onyx.connect()` in React code as `withOnyx()` will delay the rendering of the wrapped component until all keys have been accessed and made available.
134
+
135
+ ## Clean up
136
+
137
+ To clear all data from `Onyx` we can use `Onyx.clear()`.
138
+
139
+ ```javascript
140
+ function signOut() {
141
+ Onyx.clear();
142
+ }
143
+ ```
144
+
145
+ ## Storage Providers
146
+ Onyx.get/set and the rest of the API accesses the underlying storage
147
+ differently depending on the platform
148
+
149
+ Under the hood storage access calls are delegated to a [`StorageProvider`](lib/storage/index.web.js)
150
+ Some platforms (like web and desktop) might use the same storage provider
151
+
152
+ If a platform needs to use a separate library (like using MMVK for react-native) it should be added in the following way:
153
+ 1. Create a `StorageProvider.js` at [lib/storage/providers](lib/storage/providers)
154
+ Reference an existing [StorageProvider](lib/storage/providers/AsyncStorage.js) for the interface that has to be implemented
155
+ 2. Update the factory at [lib/storage/index.web.js](lib/storage/index.web.js) and [lib/storage/index.native.js](lib/storage/index.native.js) to return the newly created Provider for the desired Platform(s)
156
+
157
+ # API Reference
158
+
159
+ [Docs](./API.md)
160
+
161
+ # Storage Eviction
162
+
163
+ Different platforms come with varying storage capacities and Onyx has a way to gracefully fail when those storage limits are encountered. When Onyx fails to set or modify a key the following steps are taken:
164
+ 1. Onyx looks at a list of recently accessed keys (access is defined as subscribed to or modified) and locates the key that was least recently accessed
165
+ 2. It then deletes this key and retries the original operation
166
+
167
+ By default, Onyx will not evict anything from storage and will presume all keys are "unsafe" to remove unless explicitly told otherwise.
168
+
169
+ **To flag a key as safe for removal:**
170
+ - Add the key to the `safeEvictionKeys` option in `Onyx.init(options)`
171
+ - Implement `canEvict` in the Onyx config for each component subscribing to a key
172
+ - The key will only be deleted when all subscribers return `true` for `canEvict`
173
+
174
+ e.g.
175
+ ```js
176
+ Onyx.init({
177
+ safeEvictionKeys: [ONYXKEYS.COLLECTION.REPORT_ACTIONS],
178
+ });
179
+ ```
180
+
181
+ ```js
182
+ export default withOnyx({
183
+ reportActions: {
184
+ key: ({reportID}) => `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}_`,
185
+ canEvict: props => !props.isActiveReport,
186
+ },
187
+ })(ReportActionsView);
188
+ ```
189
+
190
+ # Benchmarks
191
+
192
+ Provide the `captureMetrics` boolean flag to `Onyx.init` to capture call statistics
193
+
194
+ ```js
195
+ Onyx.init({
196
+ keys: ONYXKEYS,
197
+ safeEvictionKeys: [ONYXKEYS.COLLECTION.REPORT_ACTIONS],
198
+ captureMetrics: Config.BENCHMARK_ONYX,
199
+ });
200
+ ```
201
+
202
+ At any point you can get the collected statistics using `Onyx.getMetrics()`.
203
+ This will return an object containing `totalTime`, `averageTime` and `summaries`.
204
+ `summaries` is a collection of statistics for each method it contains data about:
205
+ - method name
206
+ - total, max, min, average times for this method calls
207
+ - calls - a list of individual calls with each having: start time; end time; call duration; call arguments
208
+ - start/end times are relative to application launch time - 0.00 being exactly at launch
209
+
210
+ If you wish to reset the metrics and start over use `Onyx.resetMetrics()`
211
+
212
+ Finally, there's a `Onyx.printMetrics()` method which prints human statistics information on the dev console. You can use this method during debugging. For example add an `Onyx.printMetrics()` line somewhere in code or call it through the dev console. It supports 3 popular formats *MD* - human friendly markdown, *CSV* and *JSON*. The default is MD if you want to print another format call `Onyx.printMetrics({ format: 'csv' })` or
213
+ `Onyx.printMetrics({ format: 'json' })`.
214
+
215
+ Sample output of `Onyx.printMetrics()`
216
+
217
+ ```
218
+ ### Onyx Benchmark
219
+ - Total: 1.5min
220
+ - Last call finished at: 12.55sec
221
+
222
+ | method | total time spent | max | min | avg | time last call completed | calls made |
223
+ |-----------------|-----------------:|----------:|---------:|----------:|-------------------------:|-----------:|
224
+ | Onyx:getAllKeys | 1.2min | 2.16sec | 0.159ms | 782.230ms | 12.55sec | 90 |
225
+ | Onyx:merge | 4.73sec | 2.00sec | 74.412ms | 591.642ms | 10.24sec | 8 |
226
+ | Onyx:set | 3.90sec | 846.760ms | 43.663ms | 433.056ms | 7.47sec | 9 |
227
+ | Onyx:get | 8.87sec | 2.00sec | 0.063ms | 61.998ms | 10.24sec | 143 |
228
+
229
+
230
+ | Onyx:set |
231
+ |---------------------------------------------------------------|
232
+ | start time | end time | duration | args |
233
+ |-----------:|----------:|----------:|--------------------------|
234
+ | 291.042ms | 553.079ms | 262.037ms | session, [object Object] |
235
+ | 293.719ms | 553.316ms | 259.597ms | account, [object Object] |
236
+ | 294.541ms | 553.651ms | 259.109ms | network, [object Object] |
237
+ | 365.378ms | 554.246ms | 188.867ms | iou, [object Object] |
238
+ | 1.08sec | 2.20sec | 1.12sec | network, [object Object] |
239
+ | 1.08sec | 2.20sec | 1.12sec | iou, [object Object] |
240
+ | 1.17sec | 2.20sec | 1.03sec | currentURL, / |
241
+ ```
242
+
243
+
244
+ # Development
245
+
246
+ `react-native` bundles source using the `metro` bundler. `metro` does not follow symlinks, so we can't use `npm link` to
247
+ link a local version of Onyx during development
248
+
249
+ To quickly test small changes you can directly go to `node_modules/react-native-onyx` in the parent project and:
250
+ - tweak original source if you're testing over a react-native project
251
+ - tweak `dist/web.development.js` for non react-native-projects
252
+
253
+ To continuously work on Onyx we have to set up a task that copies content to parent project's `node_modules/react-native-onyx`:
254
+ 1. Work on Onyx feature or a fix
255
+ 2. Save files
256
+ 3. Optional: run `npm build:web` (if you're working or want to test on a non react-native project)
257
+ - `npm link` would actually work outside of `react-native` and it can be used to link Onyx locally for a web only project
258
+ 4. Copy Onyx to consumer project's `node_modules/react-native-onyx`