124 | }
125 | })
126 | ```
127 |
128 | ### `pure(Component).withEquality(eq)`
129 |
130 | Same as using `pure(Component)` but with a custom equality function which is
131 | used to compare props/state and reactive values.
132 |
133 | Useful when using with libraries like [Immutable.js][immutable] which provide
134 | its equality definition:
135 |
136 | ```js
137 | import * as Immutable from 'immutable'
138 | import {pure} from 'react-derivable'
139 |
140 | let Pure = pure(Component).withEquality(Immutable.is)
141 | ```
142 |
143 | ## Guides
144 |
145 | ### Local component state
146 |
147 | React has its own facilities for managing local component state. In my mind it
148 | is much more convenient to have the same mechanism serve both local component
149 | state and global app state management needs. That way composing code which uses
150 | different state values and updates becomes much easier. Also refactorings which
151 | change from where state is originated from are frictionless with this approach.
152 |
153 | As any component produced with `reactive(Component)` reacts on changes to
154 | reactive values dereferenced in its `render()` method we can take advantage of
155 | this.
156 |
157 | Just store some atom on a component instance and use it to render UI and update
158 | its value when needed.
159 |
160 | That's all it takes to introduce local component state:
161 |
162 | ```js
163 | import {Component} from 'react'
164 | import {atom} from 'derivable'
165 | import {reactive} from 'react-derivable'
166 |
167 | class Counter extends Component {
168 |
169 | counter = atom(1)
170 |
171 | onClick = () =>
172 | this.counter.swap(value => value + 1)
173 |
174 | render() {
175 | return (
176 |
177 |
{this.counter.get()}
178 |
179 |
180 | )
181 | }
182 | }
183 |
184 | Counter = reactive(Counter)
185 | ```
186 |
187 | ### Flux/Redux-like unidirectional data flow
188 |
189 | Flux (or more Redux) like architecture can be implemented easily with reactive
190 | values.
191 |
192 | You would need to create a Flux architecture blueprint as a function which
193 | initialises an atom with some initial state and sets up action dispatching as a
194 | reducer (a-la Redux):
195 |
196 | ```js
197 | import {atom} from 'derivable'
198 |
199 | function createApp(transformWithAction, initialState = {}) {
200 | let state = atom(initialState)
201 | return {
202 | state: state.derive(state => state),
203 | dispatch(action) {
204 | let transform = transformWithAction[action.type]
205 | state.swap(state => transform(state, action))
206 | }
207 | }
208 | }
209 | ```
210 |
211 | Now we can use `createApp()` function to define an application in terms of
212 | initial state and actions which transform application state:
213 |
214 | ```js
215 | const CREATE_TODO = 'create-todo'
216 |
217 | let todoApp = createApp(
218 | {
219 | [CREATE_TODO](state, action) {
220 | let todoList = state.todoList.concat({text: action.text})
221 | return {...state, todoList}
222 | }
223 | },
224 | {todoList: []}
225 | )
226 |
227 | function createTodo(text) {
228 | todoApp.dispatch({type: CREATE_TODO, text})
229 | }
230 | ```
231 |
232 | Now it is easy to render app state into UI and subscribe to state changes
233 | through the `reactive(Component)` decorator:
234 |
235 | ```js
236 | import React from 'react'
237 | import {reactive} from 'react-derivable'
238 |
239 | let App = reactive(() =>
240 |
241 | {todoApp.state.get().todoList.map(item =>
{item.text}
)}
242 |
243 | )
244 | ```
245 |
246 | ### Binding to external state sources
247 |
248 | Sometimes state is originated not from application but from some external
249 | sources. One notorious example is routing where state is stored and partially
250 | controlled by a browser.
251 |
252 | It is still useful to have access to that state and do it using the homogenous
253 | API.
254 |
255 | Like we already discovered we can use derivable library to implement local
256 | component state and flux like state management easily. Let's see how we can use
257 | derivable to implement routing based on browser navigation state (HTML5
258 | pushState API).
259 |
260 | We'll be using the [history][] npm package which makes working with HTML5 API
261 | smooth and simple.
262 |
263 | First step is to make a history object which will hold the navigation state and
264 | some methods to influence those state:
265 |
266 | ```js
267 | import {createHistory as createBaseHistory} from 'history'
268 | import {atom} from 'derivable'
269 |
270 | function createHistory(options) {
271 | let history = createBaseHistory(options)
272 | let location = atom(history.getCurrentLocation())
273 | history.listen(loc => location.set(loc));
274 | history.location = location.derive(location => location)
275 | return history
276 | }
277 |
278 | let history = createHistory()
279 | ```
280 |
281 | Now to build the router we just need to use `history.location` value in
282 | `render()`:
283 |
284 | ```js
285 | let Router = reactive(props => {
286 | let {pathname} = history.location.get()
287 | // Any complex pathname matching logic here, really.
288 | if (pathname === '/') {
289 | return
290 | } else if (pathname === '/about') {
291 | return
292 | } else {
293 | return
294 | }
295 | })
296 | ```
297 |
298 | Now to change location you would need another component which transforms
299 | location state: Link. Also it could track "active" state (if link's location is
300 | the current location):
301 |
302 | ```js
303 | let Link = reactive(props => {
304 | let {pathname} = history.location.get()
305 | let className = pathname == props.href ? 'active' : ''
306 | let onClick = e => {
307 | e.preventDefault()
308 | history.push(props.href)
309 | }
310 | return
311 | })
312 | ```
313 |
314 | ### Lifting regular React components to work with derivable values
315 |
316 | If you already have a React component which works with regular JS values but
317 | want it to work with derivable values you can use this little trick:
318 |
319 | ```js
320 | import {atom, unpack} from 'derivable'
321 | import {reactive} from 'react-derivable'
322 |
323 | class Hello extends React.Component {
324 |
325 | render() {
326 | return
{this.props.message}
327 | }
328 | }
329 |
330 | let ReactiveHello = reactive(props =>
331 | )
332 |
333 |
334 | ```
335 |
336 | Also because you are passing values as plain props they are going to participate
337 | in React component lifecycle as usual (e.g. you can access prev values in
338 | `componentDidUpdate`):
339 |
340 | ```js
341 | class Hello extends React.Component {
342 |
343 | render() {
344 | return
63 | );
64 | });
65 |
66 | /**
67 | * is a pure reactive component.
68 | *
69 | * Pure means that its value depends on only reactive values dereferences in its
70 | * render method. Even if app re-renders it won't re-render unless `counter`
71 | * prop value changes.
72 | */
73 |
74 | let Counter = pure(props => {
75 | console.log(`render: `);
76 | return (
77 |
78 |
79 | Value: {props.counter.get()}
80 |
81 |
82 |
83 |
84 | );
85 | });
86 |
87 | /**
88 | * Render application into DOM.
89 | */
90 |
91 | let render = () =>
92 | ReactDOM.render(
93 | ,
94 | document.getElementById('root')
95 | );
96 |
97 | /**
98 | * This is not required to use React and Derivable!
99 | *
100 | * This helps webpack reload application when source code changes.
101 | */
102 | if (module.hot) {
103 | module.hot.accept();
104 | render();
105 | }
106 |
--------------------------------------------------------------------------------
/flow-typed/jest_v20.x.x.js:
--------------------------------------------------------------------------------
1 | type JestMockFn = {
2 | (...args: Array): any,
3 | /**
4 | * An object for introspecting mock calls
5 | */
6 | mock: {
7 | /**
8 | * An array that represents all calls that have been made into this mock
9 | * function. Each call is represented by an array of arguments that were
10 | * passed during the call.
11 | */
12 | calls: Array>,
13 | /**
14 | * An array that contains all the object instances that have been
15 | * instantiated from this mock function.
16 | */
17 | instances: mixed
18 | },
19 | /**
20 | * Resets all information stored in the mockFn.mock.calls and
21 | * mockFn.mock.instances arrays. Often this is useful when you want to clean
22 | * up a mock's usage data between two assertions.
23 | */
24 | mockClear(): Function,
25 | /**
26 | * Resets all information stored in the mock. This is useful when you want to
27 | * completely restore a mock back to its initial state.
28 | */
29 | mockReset(): Function,
30 | /**
31 | * Removes the mock and restores the initial implementation. This is useful
32 | * when you want to mock functions in certain test cases and restore the
33 | * original implementation in others. Beware that mockFn.mockRestore only
34 | * works when mock was created with jest.spyOn. Thus you have to take care of
35 | * restoration yourself when manually assigning jest.fn().
36 | */
37 | mockRestore(): Function,
38 | /**
39 | * Accepts a function that should be used as the implementation of the mock.
40 | * The mock itself will still record all calls that go into and instances
41 | * that come from itself -- the only difference is that the implementation
42 | * will also be executed when the mock is called.
43 | */
44 | mockImplementation(fn: Function): JestMockFn,
45 | /**
46 | * Accepts a function that will be used as an implementation of the mock for
47 | * one call to the mocked function. Can be chained so that multiple function
48 | * calls produce different results.
49 | */
50 | mockImplementationOnce(fn: Function): JestMockFn,
51 | /**
52 | * Just a simple sugar function for returning `this`
53 | */
54 | mockReturnThis(): void,
55 | /**
56 | * Deprecated: use jest.fn(() => value) instead
57 | */
58 | mockReturnValue(value: any): JestMockFn,
59 | /**
60 | * Sugar for only returning a value once inside your mock
61 | */
62 | mockReturnValueOnce(value: any): JestMockFn
63 | };
64 |
65 | type JestAsymmetricEqualityType = {
66 | /**
67 | * A custom Jasmine equality tester
68 | */
69 | asymmetricMatch(value: mixed): boolean
70 | };
71 |
72 | type JestCallsType = {
73 | allArgs(): mixed,
74 | all(): mixed,
75 | any(): boolean,
76 | count(): number,
77 | first(): mixed,
78 | mostRecent(): mixed,
79 | reset(): void
80 | };
81 |
82 | type JestClockType = {
83 | install(): void,
84 | mockDate(date: Date): void,
85 | tick(milliseconds?: number): void,
86 | uninstall(): void
87 | };
88 |
89 | type JestMatcherResult = {
90 | message?: string | (() => string),
91 | pass: boolean
92 | };
93 |
94 | type JestMatcher = (actual: any, expected: any) => JestMatcherResult;
95 |
96 | type JestPromiseType = {
97 | /**
98 | * Use rejects to unwrap the reason of a rejected promise so any other
99 | * matcher can be chained. If the promise is fulfilled the assertion fails.
100 | */
101 | rejects: JestExpectType,
102 | /**
103 | * Use resolves to unwrap the value of a fulfilled promise so any other
104 | * matcher can be chained. If the promise is rejected the assertion fails.
105 | */
106 | resolves: JestExpectType
107 | };
108 |
109 | /**
110 | * Plugin: jest-enzyme
111 | */
112 | type EnzymeMatchersType = {
113 | toBeChecked(): void,
114 | toBeDisabled(): void,
115 | toBeEmpty(): void,
116 | toBePresent(): void,
117 | toContainReact(component: React$Element): void,
118 | toHaveClassName(className: string): void,
119 | toHaveHTML(html: string): void,
120 | toHaveProp(propKey: string, propValue?: any): void,
121 | toHaveRef(refName: string): void,
122 | toHaveState(stateKey: string, stateValue?: any): void,
123 | toHaveStyle(styleKey: string, styleValue?: any): void,
124 | toHaveTagName(tagName: string): void,
125 | toHaveText(text: string): void,
126 | toIncludeText(text: string): void,
127 | toHaveValue(value: any): void,
128 | toMatchSelector(selector: string): void,
129 | };
130 |
131 | type JestExpectType = {
132 | not: JestExpectType & EnzymeMatchersType,
133 | /**
134 | * If you have a mock function, you can use .lastCalledWith to test what
135 | * arguments it was last called with.
136 | */
137 | lastCalledWith(...args: Array): void,
138 | /**
139 | * toBe just checks that a value is what you expect. It uses === to check
140 | * strict equality.
141 | */
142 | toBe(value: any): void,
143 | /**
144 | * Use .toHaveBeenCalled to ensure that a mock function got called.
145 | */
146 | toBeCalled(): void,
147 | /**
148 | * Use .toBeCalledWith to ensure that a mock function was called with
149 | * specific arguments.
150 | */
151 | toBeCalledWith(...args: Array): void,
152 | /**
153 | * Using exact equality with floating point numbers is a bad idea. Rounding
154 | * means that intuitive things fail.
155 | */
156 | toBeCloseTo(num: number, delta: any): void,
157 | /**
158 | * Use .toBeDefined to check that a variable is not undefined.
159 | */
160 | toBeDefined(): void,
161 | /**
162 | * Use .toBeFalsy when you don't care what a value is, you just want to
163 | * ensure a value is false in a boolean context.
164 | */
165 | toBeFalsy(): void,
166 | /**
167 | * To compare floating point numbers, you can use toBeGreaterThan.
168 | */
169 | toBeGreaterThan(number: number): void,
170 | /**
171 | * To compare floating point numbers, you can use toBeGreaterThanOrEqual.
172 | */
173 | toBeGreaterThanOrEqual(number: number): void,
174 | /**
175 | * To compare floating point numbers, you can use toBeLessThan.
176 | */
177 | toBeLessThan(number: number): void,
178 | /**
179 | * To compare floating point numbers, you can use toBeLessThanOrEqual.
180 | */
181 | toBeLessThanOrEqual(number: number): void,
182 | /**
183 | * Use .toBeInstanceOf(Class) to check that an object is an instance of a
184 | * class.
185 | */
186 | toBeInstanceOf(cls: Class<*>): void,
187 | /**
188 | * .toBeNull() is the same as .toBe(null) but the error messages are a bit
189 | * nicer.
190 | */
191 | toBeNull(): void,
192 | /**
193 | * Use .toBeTruthy when you don't care what a value is, you just want to
194 | * ensure a value is true in a boolean context.
195 | */
196 | toBeTruthy(): void,
197 | /**
198 | * Use .toBeUndefined to check that a variable is undefined.
199 | */
200 | toBeUndefined(): void,
201 | /**
202 | * Use .toContain when you want to check that an item is in a list. For
203 | * testing the items in the list, this uses ===, a strict equality check.
204 | */
205 | toContain(item: any): void,
206 | /**
207 | * Use .toContainEqual when you want to check that an item is in a list. For
208 | * testing the items in the list, this matcher recursively checks the
209 | * equality of all fields, rather than checking for object identity.
210 | */
211 | toContainEqual(item: any): void,
212 | /**
213 | * Use .toEqual when you want to check that two objects have the same value.
214 | * This matcher recursively checks the equality of all fields, rather than
215 | * checking for object identity.
216 | */
217 | toEqual(value: any): void,
218 | /**
219 | * Use .toHaveBeenCalled to ensure that a mock function got called.
220 | */
221 | toHaveBeenCalled(): void,
222 | /**
223 | * Use .toHaveBeenCalledTimes to ensure that a mock function got called exact
224 | * number of times.
225 | */
226 | toHaveBeenCalledTimes(number: number): void,
227 | /**
228 | * Use .toHaveBeenCalledWith to ensure that a mock function was called with
229 | * specific arguments.
230 | */
231 | toHaveBeenCalledWith(...args: Array): void,
232 | /**
233 | * Use .toHaveBeenLastCalledWith to ensure that a mock function was last called
234 | * with specific arguments.
235 | */
236 | toHaveBeenLastCalledWith(...args: Array): void,
237 | /**
238 | * Check that an object has a .length property and it is set to a certain
239 | * numeric value.
240 | */
241 | toHaveLength(number: number): void,
242 | /**
243 | *
244 | */
245 | toHaveProperty(propPath: string, value?: any): void,
246 | /**
247 | * Use .toMatch to check that a string matches a regular expression.
248 | */
249 | toMatch(regexp: RegExp): void,
250 | /**
251 | * Use .toMatchObject to check that a javascript object matches a subset of the properties of an object.
252 | */
253 | toMatchObject(object: Object): void,
254 | /**
255 | * This ensures that a React component matches the most recent snapshot.
256 | */
257 | toMatchSnapshot(name?: string): void,
258 | /**
259 | * Use .toThrow to test that a function throws when it is called.
260 | * If you want to test that a specific error gets thrown, you can provide an
261 | * argument to toThrow. The argument can be a string for the error message,
262 | * a class for the error, or a regex that should match the error.
263 | *
264 | * Alias: .toThrowError
265 | */
266 | toThrow(message?: string | Error | RegExp): void,
267 | toThrowError(message?: string | Error | RegExp): void,
268 | /**
269 | * Use .toThrowErrorMatchingSnapshot to test that a function throws a error
270 | * matching the most recent snapshot when it is called.
271 | */
272 | toThrowErrorMatchingSnapshot(): void
273 | };
274 |
275 | type JestObjectType = {
276 | /**
277 | * Disables automatic mocking in the module loader.
278 | *
279 | * After this method is called, all `require()`s will return the real
280 | * versions of each module (rather than a mocked version).
281 | */
282 | disableAutomock(): JestObjectType,
283 | /**
284 | * An un-hoisted version of disableAutomock
285 | */
286 | autoMockOff(): JestObjectType,
287 | /**
288 | * Enables automatic mocking in the module loader.
289 | */
290 | enableAutomock(): JestObjectType,
291 | /**
292 | * An un-hoisted version of enableAutomock
293 | */
294 | autoMockOn(): JestObjectType,
295 | /**
296 | * Clears the mock.calls and mock.instances properties of all mocks.
297 | * Equivalent to calling .mockClear() on every mocked function.
298 | */
299 | clearAllMocks(): JestObjectType,
300 | /**
301 | * Resets the state of all mocks. Equivalent to calling .mockReset() on every
302 | * mocked function.
303 | */
304 | resetAllMocks(): JestObjectType,
305 | /**
306 | * Removes any pending timers from the timer system.
307 | */
308 | clearAllTimers(): void,
309 | /**
310 | * The same as `mock` but not moved to the top of the expectation by
311 | * babel-jest.
312 | */
313 | doMock(moduleName: string, moduleFactory?: any): JestObjectType,
314 | /**
315 | * The same as `unmock` but not moved to the top of the expectation by
316 | * babel-jest.
317 | */
318 | dontMock(moduleName: string): JestObjectType,
319 | /**
320 | * Returns a new, unused mock function. Optionally takes a mock
321 | * implementation.
322 | */
323 | fn(implementation?: Function): JestMockFn,
324 | /**
325 | * Determines if the given function is a mocked function.
326 | */
327 | isMockFunction(fn: Function): boolean,
328 | /**
329 | * Given the name of a module, use the automatic mocking system to generate a
330 | * mocked version of the module for you.
331 | */
332 | genMockFromModule(moduleName: string): any,
333 | /**
334 | * Mocks a module with an auto-mocked version when it is being required.
335 | *
336 | * The second argument can be used to specify an explicit module factory that
337 | * is being run instead of using Jest's automocking feature.
338 | *
339 | * The third argument can be used to create virtual mocks -- mocks of modules
340 | * that don't exist anywhere in the system.
341 | */
342 | mock(
343 | moduleName: string,
344 | moduleFactory?: any,
345 | options?: Object
346 | ): JestObjectType,
347 | /**
348 | * Resets the module registry - the cache of all required modules. This is
349 | * useful to isolate modules where local state might conflict between tests.
350 | */
351 | resetModules(): JestObjectType,
352 | /**
353 | * Exhausts the micro-task queue (usually interfaced in node via
354 | * process.nextTick).
355 | */
356 | runAllTicks(): void,
357 | /**
358 | * Exhausts the macro-task queue (i.e., all tasks queued by setTimeout(),
359 | * setInterval(), and setImmediate()).
360 | */
361 | runAllTimers(): void,
362 | /**
363 | * Exhausts all tasks queued by setImmediate().
364 | */
365 | runAllImmediates(): void,
366 | /**
367 | * Executes only the macro task queue (i.e. all tasks queued by setTimeout()
368 | * or setInterval() and setImmediate()).
369 | */
370 | runTimersToTime(msToRun: number): void,
371 | /**
372 | * Executes only the macro-tasks that are currently pending (i.e., only the
373 | * tasks that have been queued by setTimeout() or setInterval() up to this
374 | * point)
375 | */
376 | runOnlyPendingTimers(): void,
377 | /**
378 | * Explicitly supplies the mock object that the module system should return
379 | * for the specified module. Note: It is recommended to use jest.mock()
380 | * instead.
381 | */
382 | setMock(moduleName: string, moduleExports: any): JestObjectType,
383 | /**
384 | * Indicates that the module system should never return a mocked version of
385 | * the specified module from require() (e.g. that it should always return the
386 | * real module).
387 | */
388 | unmock(moduleName: string): JestObjectType,
389 | /**
390 | * Instructs Jest to use fake versions of the standard timer functions
391 | * (setTimeout, setInterval, clearTimeout, clearInterval, nextTick,
392 | * setImmediate and clearImmediate).
393 | */
394 | useFakeTimers(): JestObjectType,
395 | /**
396 | * Instructs Jest to use the real versions of the standard timer functions.
397 | */
398 | useRealTimers(): JestObjectType,
399 | /**
400 | * Creates a mock function similar to jest.fn but also tracks calls to
401 | * object[methodName].
402 | */
403 | spyOn(object: Object, methodName: string): JestMockFn
404 | };
405 |
406 | type JestSpyType = {
407 | calls: JestCallsType
408 | };
409 |
410 | /** Runs this function after every test inside this context */
411 | declare function afterEach(fn: Function): void;
412 | /** Runs this function before every test inside this context */
413 | declare function beforeEach(fn: Function): void;
414 | /** Runs this function after all tests have finished inside this context */
415 | declare function afterAll(fn: Function): void;
416 | /** Runs this function before any tests have started inside this context */
417 | declare function beforeAll(fn: Function): void;
418 | /** A context for grouping tests together */
419 | declare function describe(name: string, fn: Function): void;
420 |
421 | /** An individual test unit */
422 | declare var it: {
423 | /**
424 | * An individual test unit
425 | *
426 | * @param {string} Name of Test
427 | * @param {Function} Test
428 | */
429 | (name: string, fn?: Function): ?Promise,
430 | /**
431 | * Only run this test
432 | *
433 | * @param {string} Name of Test
434 | * @param {Function} Test
435 | */
436 | only(name: string, fn?: Function): ?Promise,
437 | /**
438 | * Skip running this test
439 | *
440 | * @param {string} Name of Test
441 | * @param {Function} Test
442 | */
443 | skip(name: string, fn?: Function): ?Promise,
444 | /**
445 | * Run the test concurrently
446 | *
447 | * @param {string} Name of Test
448 | * @param {Function} Test
449 | */
450 | concurrent(name: string, fn?: Function): ?Promise
451 | };
452 | declare function fit(name: string, fn: Function): ?Promise;
453 | /** An individual test unit */
454 | declare var test: typeof it;
455 | /** A disabled group of tests */
456 | declare var xdescribe: typeof describe;
457 | /** A focused group of tests */
458 | declare var fdescribe: typeof describe;
459 | /** A disabled individual test */
460 | declare var xit: typeof it;
461 | /** A disabled individual test */
462 | declare var xtest: typeof it;
463 |
464 | /** The expect function is used every time you want to test a value */
465 | declare var expect: {
466 | /** The object that you want to make assertions against */
467 | (value: any): JestExpectType & JestPromiseType & EnzymeMatchersType,
468 | /** Add additional Jasmine matchers to Jest's roster */
469 | extend(matchers: { [name: string]: JestMatcher }): void,
470 | /** Add a module that formats application-specific data structures. */
471 | addSnapshotSerializer(serializer: (input: Object) => string): void,
472 | assertions(expectedAssertions: number): void,
473 | hasAssertions(): void,
474 | any(value: mixed): JestAsymmetricEqualityType,
475 | anything(): void,
476 | arrayContaining(value: Array): void,
477 | objectContaining(value: Object): void,
478 | /** Matches any received string that contains the exact expected string. */
479 | stringContaining(value: string): void,
480 | stringMatching(value: string | RegExp): void
481 | };
482 |
483 | // TODO handle return type
484 | // http://jasmine.github.io/2.4/introduction.html#section-Spies
485 | declare function spyOn(value: mixed, method: string): Object;
486 |
487 | /** Holds all functions related to manipulating test runner */
488 | declare var jest: JestObjectType;
489 |
490 | /**
491 | * The global Jamine object, this is generally not exposed as the public API,
492 | * using features inside here could break in later versions of Jest.
493 | */
494 | declare var jasmine: {
495 | DEFAULT_TIMEOUT_INTERVAL: number,
496 | any(value: mixed): JestAsymmetricEqualityType,
497 | anything(): void,
498 | arrayContaining(value: Array): void,
499 | clock(): JestClockType,
500 | createSpy(name: string): JestSpyType,
501 | createSpyObj(
502 | baseName: string,
503 | methodNames: Array
504 | ): { [methodName: string]: JestSpyType },
505 | objectContaining(value: Object): void,
506 | stringMatching(value: string): void
507 | };
508 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-derivable",
3 | "version": "0.4.0",
4 | "description": "Make React component react on changes in referened derivable values",
5 | "main": "lib/index.js",
6 | "files": [
7 | "lib"
8 | ],
9 | "scripts": {
10 | "test": "make lint test"
11 | },
12 | "repository": {
13 | "type": "git",
14 | "url": "git+https://github.com/andreypopp/react-derivable.git"
15 | },
16 | "keywords": [
17 | "react",
18 | "derivable"
19 | ],
20 | "author": "Andrey Popp <8mayday@gmail.com>",
21 | "license": "MIT",
22 | "bugs": {
23 | "url": "https://github.com/andreypopp/react-derivable/issues"
24 | },
25 | "homepage": "https://github.com/andreypopp/react-derivable#readme",
26 | "peerDependencies": {
27 | "derivable": "2.0.0-beta.2",
28 | "react": ">= 0.14.0 < 16.0.0"
29 | },
30 | "devDependencies": {
31 | "babel-cli": "^6.10.1",
32 | "babel-core": "^6.10.4",
33 | "babel-jest": "^20.0.3",
34 | "babel-preset-prometheusresearch": "^0.1.0",
35 | "benchmark": "^2.1.0",
36 | "create-react-app": "^0.2.0",
37 | "derivable": "2.0.0-beta.2",
38 | "doctoc": "^1.2.0",
39 | "eslint": "^3.0.1",
40 | "eslint-config-prometheusresearch": "^0.2.0",
41 | "eslint-plugin-react": "^5.2.2",
42 | "flow-bin": "^0.37.0",
43 | "immutable": "^3.8.1",
44 | "jest-cli": "^20.0.4",
45 | "prettier": "^1.5.3",
46 | "react": "^15.2.1",
47 | "react-addons-test-utils": "^15.4.1",
48 | "react-dom": "^15.2.1"
49 | },
50 | "dependencies": {
51 | "invariant": "^2.2.1",
52 | "prop-types": "^15.5.10"
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/__tests__/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "mocha": true
4 | },
5 | "globals": {
6 | "assert": true,
7 | "sinon": true
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/__tests__/index-test.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @copyright 2016-present, Prometheus Research, LLC
3 | */
4 |
5 | import React from 'react';
6 | import ReactDOM from 'react-dom';
7 | import PropTypes from 'prop-types';
8 | import {atom} from 'derivable';
9 | import * as Immutable from 'immutable';
10 |
11 | import {shallowEqual, reactive, pure} from '../index';
12 |
13 | function markup(element) {
14 | return element.innerHTML.replace(/ data\-reactroot="[^"]*"/g, '');
15 | }
16 |
17 | describe('react-derivable', function() {
18 | describe('shallowEqual', function() {
19 | it('compares two object for eq', function() {
20 | expect(shallowEqual({}, {})).toBe(true);
21 | expect(shallowEqual({a: 1}, {a: 1})).toBe(true);
22 | expect(shallowEqual({a: 1}, {})).toBe(false);
23 | expect(shallowEqual({a: 1}, {a: 2})).toBe(false);
24 | expect(shallowEqual({a: {}}, {a: {}})).toBe(false);
25 | expect(shallowEqual(1, 2)).toBe(false);
26 | expect(shallowEqual(1, 1)).toBe(true);
27 | expect(shallowEqual(null, null)).toBe(true);
28 | expect(shallowEqual(undefined, undefined)).toBe(true);
29 | expect(shallowEqual('', '')).toBe(true);
30 | expect(shallowEqual(true, true)).toBe(true);
31 | expect(shallowEqual(false, false)).toBe(true);
32 | });
33 |
34 | it('compares reactive values', function() {
35 | let a1 = atom(1);
36 | let a2 = atom(1);
37 | let onDerivableReplace = jest.fn();
38 | expect(shallowEqual({a: a1}, {a: a2}, undefined, onDerivableReplace)).toBe(true);
39 | expect(onDerivableReplace.mock.calls.length).toBe(1);
40 | expect(onDerivableReplace.mock.calls[0]).toEqual([a1, a2]);
41 | a2.set(2);
42 | expect(shallowEqual({a: a1}, {a: a2}), undefined, onDerivableReplace).toBe(false);
43 | expect(onDerivableReplace.mock.calls.length).toBe(1);
44 | });
45 |
46 | it('compares reactive values w/ diff eq', function() {
47 | let almostEq = (a, b) => Math.abs(a - b) < 5;
48 | let a1 = atom(1).withEquality(almostEq);
49 | let a2 = atom(1);
50 | let onDerivableReplace = jest.fn();
51 | expect(shallowEqual({a: a1}, {a: a2}, undefined, onDerivableReplace)).toBe(false);
52 | expect(onDerivableReplace.mock.calls.length).toBe(0);
53 | });
54 |
55 | it('compares reactive values w/ custom eq', function() {
56 | let almostEq = (a, b) => Math.abs(a - b) < 5;
57 | let a1 = atom(1).withEquality(almostEq);
58 | let a2 = atom(1).withEquality(almostEq);
59 | let onDerivableReplace = jest.fn();
60 | expect(shallowEqual({a: a1}, {a: a2}, undefined, onDerivableReplace)).toBe(true);
61 | expect(onDerivableReplace.mock.calls.length).toBe(1);
62 | expect(onDerivableReplace.mock.calls[0]).toEqual([a1, a2]);
63 | a2.set(2);
64 | expect(shallowEqual({a: a1}, {a: a2}, undefined, onDerivableReplace)).toBe(true);
65 | expect(onDerivableReplace.mock.calls.length).toBe(2);
66 | expect(onDerivableReplace.mock.calls[1]).toEqual([a1, a2]);
67 | a2.set(20);
68 | expect(shallowEqual({a: a1}, {a: a2}, undefined, onDerivableReplace)).toBe(false);
69 | expect(onDerivableReplace.mock.calls.length).toBe(2);
70 | });
71 | });
72 |
73 | describe('decorators', function() {
74 | let renderCount;
75 | let root;
76 |
77 | class ClassBased extends React.Component {
78 | render() {
79 | let {title, message, x} = this.props;
80 | renderCount += 1;
81 | return (
82 |