Skip to content

Commit 0870587

Browse files
m.berezinm.berezin
authored andcommitted
feat(react/create(Consumer,consume,Form,Field,Componets)): React bindings reworked
Functions 'createConsume', 'createForm', 'createField' now accept Consumer. Added 'createComponents' to create all of them at once using destructuring. BREAKING CHANGE: You must pass Consumer to 'createConsume', 'createForm', 'createField' instead of Stapp instance
1 parent 5959e27 commit 0870587

File tree

13 files changed

+152
-92
lines changed

13 files changed

+152
-92
lines changed

docs/usage/react.md

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ Stapp comes with a bunch of helpers that allows using stapp application with rea
1414
- [`createConsumer()`](#createconsumer)
1515
- [`createConsume()`](#createconsume)
1616
- [`createForm()` and `createField()`](#createform-and-createfield)
17+
- [`createComponents()`](#createcomponents)
1718
- [Type definitions](#type-definitions)
1819

1920
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
@@ -80,8 +81,8 @@ const App = () => <Consumer component={ List } />
8081
### `createConsume()`
8182
8283
```typescript
83-
type createConsume = (app: Stapp) => Consume
84-
type createInject = (app: Stapp) => Consume // theese are aliases
84+
type createConsume = (Consumer: Consumer) => ConsumerHoc
85+
type createInject = (Consumer: Consumer) => ConsumerHoc // theese are aliases
8586

8687
type ConsumerHoc = (
8788
mapState?: (state, props) => any,
@@ -115,8 +116,8 @@ const App = inject(
115116
### `createForm()` and `createField()`
116117
117118
```typescript
118-
type createForm = (app: Stapp) => Form
119-
type createFiled = (app: Stapp) => Field
119+
type createForm = (Consumer: Consumer) => Form
120+
type createFiled = (Consumer: Consumer) => Field
120121

121122
type Form = React.Component<{
122123
children?: (props: FormApi) => React.ReactElement | null,
@@ -194,9 +195,21 @@ const App = () => {
194195
}
195196
```
196197
198+
### `createComponents()`
199+
200+
```typescript
201+
type createComponents = (app: Stapp) => {
202+
Consumer: Consumer,
203+
consume: ConsumerHoc,
204+
Form: Form,
205+
Field: Field
206+
}
207+
```
208+
197209
## Type definitions
198210
199211
* [`createConsumer`](/types.html#createconsumer)
200212
* [`createConsume`](/types.html#createconsume)
201213
* [`createForm`](/types.html#createform)
202214
* [`createField`](/types.html#createfield)
215+
* [`createComponents`](/types.html#createcomponents)
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { StatelessComponent } from 'react'
2+
import { ConsumerHoc } from '../createConsume/createConsume.h'
3+
import { ConsumerClass } from '../createConsumer/createConsumer.h'
4+
import { FieldProps } from '../createField/createField.h'
5+
import { FormProps } from '../createForm/createForm.h'
6+
7+
export type StappComponents<State, Api> = {
8+
Consumer: ConsumerClass<State, Api, any, any, any>
9+
consume: ConsumerHoc<State, Api>
10+
Form: StatelessComponent<FormProps>
11+
Field: StatelessComponent<FieldProps>
12+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { createApp } from '../../core/createApp/createApp'
2+
import { createReducer } from '../../core/createReducer/createReducer'
3+
import { uniqueId } from '../../helpers/uniqueId/uniqueId'
4+
import { createComponents } from './createComponents'
5+
6+
describe('createComponents', () => {
7+
const getApp = () =>
8+
createApp({
9+
name: 'test' + uniqueId(),
10+
modules: [
11+
{
12+
name: 'test',
13+
reducers: {
14+
r: createReducer(null)
15+
}
16+
}
17+
]
18+
})
19+
20+
it('should return Consumer, consume, Form and Field as object', () => {
21+
const app = getApp()
22+
23+
const { Consumer, consume, Form, Field } = createComponents(app)
24+
25+
expect(Consumer).toBeTruthy()
26+
expect(consume).toBeTruthy()
27+
expect(Form).toBeTruthy()
28+
expect(Field).toBeTruthy()
29+
})
30+
})
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { StatelessComponent } from 'react'
2+
import { createConsume, createConsumer, createField, createForm } from '..'
3+
import { Stapp } from '../../core/createApp/createApp.h'
4+
import { ConsumerHoc } from '../createConsume/createConsume.h'
5+
import { ConsumerClass } from '../createConsumer/createConsumer.h'
6+
import { FieldProps } from '../createField/createField.h'
7+
import { FormProps } from '../createForm/createForm.h'
8+
import { StappComponents } from './createComponents.h'
9+
10+
export const createComponents = <State, Api>(
11+
app: Stapp<State, Api>
12+
): StappComponents<State, Api> => {
13+
let Consumer: ConsumerClass<State, Api, any, any, any>
14+
let consume: ConsumerHoc<State, Api>
15+
let Form: StatelessComponent<FormProps>
16+
let Field: StatelessComponent<FieldProps>
17+
18+
return {
19+
get Consumer() {
20+
return Consumer || (Consumer = createConsumer(app))
21+
},
22+
23+
get consume() {
24+
return consume || (consume = createConsume(this.Consumer))
25+
},
26+
27+
get Form() {
28+
return Form || (Form = createForm(this.Consumer))
29+
},
30+
31+
get Field() {
32+
return Field || (Field = createField(this.Consumer))
33+
}
34+
}
35+
}

src/react/createConsume/createConsume.spec.tsx

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
// tslint:disable max-classes-per-file jsx-no-lambda
22
import { mount } from 'enzyme'
33
import React from 'react'
4+
import { createConsumer } from '..'
45
import { createApp } from '../../core/createApp/createApp'
56
import { createEvent } from '../../core/createEvent/createEvent'
67
import { createReducer } from '../../core/createReducer/createReducer'
78
import { uniqueId } from '../../helpers/uniqueId/uniqueId'
89
import { createConsume } from './createConsume'
910

10-
describe('createContext', () => {
11+
describe('createConsumer', () => {
1112
const initialState = {
1213
test: 0
1314
}
@@ -31,11 +32,13 @@ describe('createContext', () => {
3132
]
3233
})
3334

35+
const getConsumer = () => createConsumer(getApp())
36+
3437
it('should pass state to wrapped component', (done) => {
3538
expect.assertions(2)
3639

37-
const app = getApp()
38-
const consume = createConsume(app)
40+
const Consumer = getConsumer()
41+
const consume = createConsume(Consumer)
3942

4043
const Dummy = consume()(
4144
class extends React.Component<any> {
@@ -58,8 +61,8 @@ describe('createContext', () => {
5861
it('should use mapState', (done) => {
5962
expect.assertions(4)
6063

61-
const app = getApp()
62-
const consume = createConsume(app)
64+
const Consumer = getConsumer()
65+
const consume = createConsume(Consumer)
6366

6467
const Dummy = consume((state) => ({
6568
test: state.r1.test + 5,
@@ -91,8 +94,9 @@ describe('createContext', () => {
9194
it('should use mapApi', (done) => {
9295
expect.assertions(2)
9396

94-
const app = getApp()
95-
const consume = createConsume(app)
97+
const Consumer = getConsumer()
98+
const app = Consumer.app
99+
const consume = createConsume(Consumer)
96100

97101
const Dummy = consume(undefined, (api) => ({
98102
e2: api.e1
@@ -116,8 +120,9 @@ describe('createContext', () => {
116120
it('should accept props at mapApi', (done) => {
117121
expect.assertions(3)
118122

119-
const app = getApp()
120-
const consume = createConsume(app)
123+
const Consumer = getConsumer()
124+
const app = Consumer.app
125+
const consume = createConsume(Consumer)
121126

122127
const Dummy = consume(undefined, (api, props) => ({
123128
e1: {
@@ -147,8 +152,8 @@ describe('createContext', () => {
147152
it('should accepts props in mapState', (done) => {
148153
expect.assertions(4)
149154

150-
const app = getApp()
151-
const consume = createConsume(app)
155+
const Consumer = getConsumer()
156+
const consume = createConsume(Consumer)
152157

153158
const Dummy = consume((state, props) => ({
154159
test: state.r1.test + 5,
@@ -178,8 +183,8 @@ describe('createContext', () => {
178183
it('should accept mergeprops', (done) => {
179184
expect.assertions(4)
180185

181-
const app = getApp()
182-
const consume = createConsume(app)
186+
const Consumer = getConsumer()
187+
const consume = createConsume(Consumer)
183188

184189
const Dummy = consume(
185190
(state, props) => ({
@@ -222,8 +227,8 @@ describe('createContext', () => {
222227
it('should work with stateless components', (done) => {
223228
expect.assertions(2)
224229

225-
const app = getApp()
226-
const consume = createConsume(app)
230+
const Consumer = getConsumer()
231+
const consume = createConsume(Consumer)
227232

228233
const Dummy = consume()((props: any) => {
229234
expect(typeof props.e1).toBe('function')

src/react/createConsume/createConsume.ts

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,19 @@
1-
import { Component, ComponentType, createElement } from 'react'
1+
import { Component, ComponentClass, ComponentType, createElement } from 'react'
22
import { identity } from '../../helpers/identity/identity'
3-
import { createConsumer } from '../createConsumer/createConsumer'
43
import { defaultMergeProps } from '../helpers/defaultMergeProps'
54
import { getDisplayName } from '../helpers/getDisplayName'
65

76
// Models
8-
import { Stapp } from '../../core/createApp/createApp.h'
7+
import { ConsumerProps } from '../createConsumer/createConsumer.h'
98
import { ConsumerHoc } from './createConsume.h'
109

1110
/**
1211
* Creates higher order component, that passes state and api from a Stapp application to
1312
* a wrapped component
1413
*/
15-
export const createConsume = <State, Api>(app: Stapp<State, Api>): ConsumerHoc<State, Api> => {
16-
const Consumer = createConsumer(app)
17-
14+
export const createConsume = <State, Api>(
15+
Consumer: ComponentClass<ConsumerProps<State, Api, any, any, any>>
16+
): ConsumerHoc<State, Api> => {
1817
return (<SelectedState, SelectedApi, Result>(
1918
mapState: (state: State, props: any) => SelectedState = identity as any,
2019
mapApi: (api: Api, props: any) => SelectedApi = identity as any,

src/react/createConsumer/createConsumer.h.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import { ReactElement, ReactType } from 'react'
1+
import { ComponentClass, ReactElement, ReactType } from 'react'
2+
import { Stapp } from '../../core/createApp/createApp.h'
23

34
export type RenderProps<T> = {
45
children?: (api: T) => ReactElement<any> | null
@@ -17,3 +18,13 @@ export type ConsumerProps<State, Api, SelectedState, SelectedApi, Result> = {
1718
mapApi?: (api: Api) => SelectedApi
1819
mergeProps?: (selectedState: SelectedApi, selectedApi: SelectedApi) => Result
1920
} & RenderProps<Result>
21+
22+
/**
23+
* @typeparam State application store state
24+
* @typeparam Api application api interface
25+
*/
26+
export type ConsumerClass<State, Api, SelectedState, SelectedApi, Result> = ComponentClass<
27+
ConsumerProps<State, Api, SelectedState, SelectedApi, Result>
28+
> & {
29+
app: Stapp<State, Api>
30+
}

src/react/createConsumer/createConsumer.spec.tsx

Lines changed: 0 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -235,38 +235,4 @@ describe('createContext', () => {
235235

236236
mount(<Consumer component={DummyComponent} />)
237237
})
238-
239-
it('should memoize components', () => {
240-
const app1 = getApp()
241-
const app2 = getApp()
242-
const app3 = getApp()
243-
244-
const Consumer1 = createConsumer(app1)
245-
const Consumer2 = createConsumer(app2)
246-
const Consumer3 = createConsumer(app1)
247-
const Consumer4 = createConsumer(app3)
248-
const Consumer5 = createConsumer(app1)
249-
250-
expect(Consumer1).toBe(Consumer3)
251-
expect(Consumer1).toBe(Consumer5)
252-
expect(Consumer1).not.toBe(Consumer2)
253-
expect(Consumer1).not.toBe(Consumer4)
254-
})
255-
256-
it('should create different components for applications with same name', () => {
257-
const app1 = getApp('test')
258-
const app2 = getApp('test')
259-
const app3 = getApp('test')
260-
261-
const Consumer1 = createConsumer(app1)
262-
const Consumer2 = createConsumer(app2)
263-
const Consumer3 = createConsumer(app1)
264-
const Consumer4 = createConsumer(app3)
265-
const Consumer5 = createConsumer(app1)
266-
267-
expect(Consumer1).toBe(Consumer3)
268-
expect(Consumer1).toBe(Consumer5)
269-
expect(Consumer1).not.toBe(Consumer2)
270-
expect(Consumer1).not.toBe(Consumer4)
271-
})
272238
})

src/react/createConsumer/createConsumer.ts

Lines changed: 5 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import shallowEqual from 'fbjs/lib/shallowEqual'
22
// tslint:disable-next-line no-unused-variable // Needed for declarations
3-
import { Component, ComponentClass } from 'react'
3+
import { Component } from 'react'
44
import { debounceTime } from 'rxjs/operators/debounceTime'
55
import { distinctUntilChanged } from 'rxjs/operators/distinctUntilChanged'
66
import { map } from 'rxjs/operators/map'
@@ -14,20 +14,11 @@ import { renderPropType, selectorType } from '../helpers/propTypes'
1414
import { Subscription } from 'rxjs/Subscription'
1515
import { Stapp } from '../../core/createApp/createApp.h'
1616
import { renderComponent } from '../helpers/renderComponent'
17-
import { ConsumerProps } from './createConsumer.h'
17+
import { ConsumerClass, ConsumerProps } from './createConsumer.h'
1818

1919
// tslint:disable-next-line no-unused-variable // Needed for declarations
2020
// import { Requireable } from 'prop-types'
2121

22-
/**
23-
* @private
24-
*/
25-
26-
const consumers = new WeakMap<
27-
Stapp<any, any>,
28-
ComponentClass<ConsumerProps<any, any, any, any, any>>
29-
>()
30-
3122
/**
3223
* @private
3324
*/
@@ -44,12 +35,10 @@ const consumerPropTypes = {
4435
*/
4536
export const createConsumer = <State, Api>(
4637
app: Stapp<State, Api>
47-
): ComponentClass<ConsumerProps<State, Api, any, any, any>> => {
48-
if (consumers.has(app)) {
49-
return consumers.get(app) as ComponentClass<ConsumerProps<any, any, any, any, any>>
50-
}
38+
): ConsumerClass<State, Api, any, any, any> => {
39+
return class Consumer extends Component<ConsumerProps<State, Api, any, any, any>> {
40+
static app = app
5141

52-
const consumer = class Consumer extends Component<ConsumerProps<State, Api, any, any, any>> {
5342
static propTypes = consumerPropTypes
5443

5544
subscription!: Subscription
@@ -115,8 +104,4 @@ export const createConsumer = <State, Api>(
115104
)
116105
}
117106
}
118-
119-
consumers.set(app, consumer)
120-
121-
return consumer
122107
}

0 commit comments

Comments
 (0)