react-rx 1.0.0-beta.4 → 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.
Files changed (171) hide show
  1. package/.idea/vcs.xml +0 -3
  2. package/.idea/workspace.xml +18 -15
  3. package/dist/cjs/WithObservable.d.ts +3 -3
  4. package/dist/cjs/WithObservable.js +3 -3
  5. package/dist/cjs/index.d.ts +3 -2
  6. package/dist/cjs/index.js +9 -6
  7. package/dist/cjs/reactiveComponent.d.ts +6 -6
  8. package/dist/cjs/reactiveComponent.js +7 -26
  9. package/dist/cjs/useAsObservable.d.ts +1 -0
  10. package/dist/cjs/useAsObservable.js +10 -33
  11. package/dist/cjs/useIsomorphicEffect.d.ts +2 -0
  12. package/dist/cjs/useIsomorphicEffect.js +5 -0
  13. package/dist/cjs/useObservable.d.ts +6 -6
  14. package/dist/cjs/useObservable.js +8 -26
  15. package/dist/cjs/useObservableCallback.d.ts +3 -0
  16. package/dist/cjs/useObservableCallback.js +24 -0
  17. package/dist/cjs/useWithObservable.d.ts +9 -0
  18. package/dist/cjs/useWithObservable.js +11 -0
  19. package/dist/cjs/utils.d.ts +3 -3
  20. package/dist/cjs/utils.js +8 -27
  21. package/dist/cjs/withPropsStream.js +3 -3
  22. package/dist/es2015/WithObservable.d.ts +3 -3
  23. package/dist/es2015/index.d.ts +3 -2
  24. package/dist/es2015/index.js +4 -2
  25. package/dist/es2015/reactiveComponent.d.ts +6 -6
  26. package/dist/es2015/reactiveComponent.js +5 -5
  27. package/dist/es2015/useAsObservable.d.ts +1 -0
  28. package/dist/es2015/useAsObservable.js +10 -14
  29. package/dist/es2015/useIsomorphicEffect.d.ts +2 -0
  30. package/dist/es2015/useIsomorphicEffect.js +2 -0
  31. package/dist/es2015/useObservable.d.ts +6 -6
  32. package/dist/es2015/useObservable.js +8 -7
  33. package/dist/es2015/useObservableCallback.d.ts +3 -0
  34. package/dist/es2015/useObservableCallback.js +19 -0
  35. package/dist/es2015/useWithObservable.d.ts +9 -0
  36. package/dist/es2015/useWithObservable.js +7 -0
  37. package/dist/es2015/utils.d.ts +3 -3
  38. package/dist/es2015/utils.js +5 -5
  39. package/dist/esm/WithObservable.d.ts +3 -3
  40. package/dist/esm/index.d.ts +3 -2
  41. package/dist/esm/index.js +4 -2
  42. package/dist/esm/reactiveComponent.d.ts +6 -6
  43. package/dist/esm/reactiveComponent.js +5 -5
  44. package/dist/esm/useAsObservable.d.ts +1 -0
  45. package/dist/esm/useAsObservable.js +10 -14
  46. package/dist/esm/useIsomorphicEffect.d.ts +2 -0
  47. package/dist/esm/useIsomorphicEffect.js +2 -0
  48. package/dist/esm/useObservable.d.ts +6 -6
  49. package/dist/esm/useObservable.js +8 -7
  50. package/dist/esm/useObservableCallback.d.ts +3 -0
  51. package/dist/esm/useObservableCallback.js +20 -0
  52. package/dist/esm/useWithObservable.d.ts +9 -0
  53. package/dist/esm/useWithObservable.js +7 -0
  54. package/dist/esm/utils.d.ts +3 -3
  55. package/dist/esm/utils.js +5 -5
  56. package/package.json +3 -3
  57. package/src/WithObservable.tsx +4 -3
  58. package/src/index.ts +5 -4
  59. package/src/reactiveComponent.ts +24 -18
  60. package/src/useAsObservable.ts +18 -8
  61. package/src/useIsomorphicEffect.ts +3 -0
  62. package/src/useObservable.ts +17 -19
  63. package/src/useObservableCallback.ts +30 -0
  64. package/src/useWithObservable.ts +23 -0
  65. package/src/utils.ts +8 -8
  66. package/jest.config.js +0 -5
  67. package/website/.babelrc +0 -21
  68. package/website/.prettierrc +0 -7
  69. package/website/README.mdx +0 -1
  70. package/website/package.json +0 -84
  71. package/website/src/@types/babel.d.ts +0 -4
  72. package/website/src/@types/mdx.d.ts +0 -11
  73. package/website/src/@types/react-css-burger.d.ts +0 -4
  74. package/website/src/App.tsx +0 -68
  75. package/website/src/components/Header.tsx +0 -118
  76. package/website/src/components/Link.tsx +0 -18
  77. package/website/src/components/LoadPage.tsx +0 -60
  78. package/website/src/components/Sidebar.tsx +0 -88
  79. package/website/src/components/logos/Github.tsx +0 -10
  80. package/website/src/components/logos/React.tsx +0 -27
  81. package/website/src/components/logos/ReactRxLogo.tsx +0 -39
  82. package/website/src/components/logos/RxJs.tsx +0 -69
  83. package/website/src/components/repl/Checkerboard.tsx +0 -25
  84. package/website/src/components/repl/CodeBlock.tsx +0 -147
  85. package/website/src/components/repl/CodeMirror.tsx +0 -37
  86. package/website/src/components/repl/CodeMirrorMode.tsx +0 -67
  87. package/website/src/components/repl/EvalCode.tsx +0 -55
  88. package/website/src/components/repl/Prelude.tsx +0 -50
  89. package/website/src/components/repl/ShowError.tsx +0 -27
  90. package/website/src/components/repl/codemirror-lib.ts +0 -8
  91. package/website/src/components/repl/codemirror-theme.css +0 -131
  92. package/website/src/components/repl/compile/babel.ts +0 -21
  93. package/website/src/components/repl/eval/evalCallback.ts +0 -27
  94. package/website/src/components/repl/eval/evalCode.ts +0 -7
  95. package/website/src/components/repl/eval/evalExpression.ts +0 -4
  96. package/website/src/components/repl/eval/evalReactDomRender.ts +0 -23
  97. package/website/src/components/repl/index.ts +0 -1
  98. package/website/src/components/repl/runMode.ts +0 -42
  99. package/website/src/datastores/location.ts +0 -14
  100. package/website/src/datastores/page.ts +0 -11
  101. package/website/src/datastores/pageTransition.ts +0 -97
  102. package/website/src/entries/main.css +0 -2
  103. package/website/src/entries/main.tsx +0 -6
  104. package/website/src/examples/FizzBuzz/FizzBuzzExample.tsx +0 -39
  105. package/website/src/examples/FizzBuzz/index.tsx +0 -9
  106. package/website/src/examples/_utils/globalScope.ts +0 -45
  107. package/website/src/examples/animation/AnimationExample.tsx +0 -152
  108. package/website/src/examples/animation/easings.ts +0 -0
  109. package/website/src/examples/animation/index.tsx +0 -15
  110. package/website/src/examples/context/Context.example.jsx +0 -67
  111. package/website/src/examples/context/index.tsx +0 -9
  112. package/website/src/examples/errors/Errors.example.jsx +0 -69
  113. package/website/src/examples/errors/index.tsx +0 -9
  114. package/website/src/examples/event-handlers/EventHandlersExample.tsx +0 -48
  115. package/website/src/examples/event-handlers/index.tsx +0 -9
  116. package/website/src/examples/fetch/FetchExample.jsx +0 -43
  117. package/website/src/examples/fetch/a.txt +0 -1
  118. package/website/src/examples/fetch/b.txt +0 -1
  119. package/website/src/examples/fetch/index.tsx +0 -9
  120. package/website/src/examples/form-data/FormDataExample.jsx +0 -119
  121. package/website/src/examples/form-data/index.tsx +0 -12
  122. package/website/src/examples/form-data/storage.ts +0 -27
  123. package/website/src/examples/forward-ref/ForwardRefExample.tsx +0 -55
  124. package/website/src/examples/forward-ref/index.tsx +0 -9
  125. package/website/src/examples/hello-world/HelloWorld.example.jsx +0 -6
  126. package/website/src/examples/hello-world/index.tsx +0 -9
  127. package/website/src/examples/hooks-interop/HooksInterop.example.tsx +0 -42
  128. package/website/src/examples/hooks-interop/index.tsx +0 -9
  129. package/website/src/examples/passthrough-props/PassThroughPropsExample.jsx +0 -57
  130. package/website/src/examples/passthrough-props/index.tsx +0 -11
  131. package/website/src/examples/reactive-state/ReactiveState.example.jsx +0 -43
  132. package/website/src/examples/reactive-state/index.ts +0 -9
  133. package/website/src/examples/readme-examples/README.mdx +0 -0
  134. package/website/src/examples/readme-examples/index.ts +0 -11
  135. package/website/src/examples/shared-state/SharedState.example.tsx +0 -59
  136. package/website/src/examples/shared-state/index.tsx +0 -9
  137. package/website/src/examples/simple/Counter.example.tsx +0 -14
  138. package/website/src/examples/simple/index.tsx +0 -9
  139. package/website/src/examples/sync/Sync.example.tsx +0 -9
  140. package/website/src/examples/sync/index.tsx +0 -9
  141. package/website/src/examples/tick/TickExample.jsx +0 -51
  142. package/website/src/examples/tick/index.ts +0 -9
  143. package/website/src/examples/todo-app/TodoApp.example.tsx +0 -88
  144. package/website/src/examples/todo-app/index.tsx +0 -12
  145. package/website/src/examples/todo-app/storage.ts +0 -27
  146. package/website/src/examples/use-element/UseElementExample.tsx +0 -70
  147. package/website/src/examples/use-element/index.tsx +0 -9
  148. package/website/src/examples/use-observable/search/SearchExample.tsx +0 -82
  149. package/website/src/examples/use-observable/search/index.tsx +0 -8
  150. package/website/src/examples/use-observable/use-observable/UseObservableExample.tsx +0 -70
  151. package/website/src/examples/use-observable/use-observable/index.tsx +0 -9
  152. package/website/src/index.html +0 -10
  153. package/website/src/mdx-components.tsx +0 -74
  154. package/website/src/pages/Api/Api.mdx +0 -134
  155. package/website/src/pages/Api/ApiPage.tsx +0 -25
  156. package/website/src/pages/Examples/Examples.tsx +0 -105
  157. package/website/src/pages/Examples/ExamplesPage.tsx +0 -13
  158. package/website/src/pages/Guide/Guide.mdx +0 -275
  159. package/website/src/pages/Guide/GuidePage.tsx +0 -25
  160. package/website/src/pages/Index/IndexPage.tsx +0 -50
  161. package/website/src/pages/Index/ReactRxReadme.md +0 -20
  162. package/website/src/pages/NotFound.tsx +0 -3
  163. package/website/src/pages/pages.ts +0 -46
  164. package/website/src/pages/styles.tsx +0 -24
  165. package/website/src/theme.ts +0 -61
  166. package/website/src/utils/parseCodeFenceHeader.ts +0 -32
  167. package/website/tsconfig.json +0 -30
  168. package/website/vercel.json +0 -4
  169. package/website/webpack/config.js +0 -5
  170. package/website/webpack/createConfig.js +0 -95
  171. package/website/webpack/mdx-toc-loader.js +0 -38
@@ -1,74 +0,0 @@
1
- import {CodeMirrorMode} from './components/repl/CodeMirrorMode'
2
-
3
- import * as React from 'react'
4
- import styled from 'styled-components'
5
- import {rxComponent} from 'react-rx'
6
- import {map} from 'rxjs/operators'
7
- import {combineLatest} from 'rxjs'
8
- import {location$} from './datastores/location'
9
- import {parseCodeFenceHeader} from './utils/parseCodeFenceHeader'
10
- import {ModeSpec} from './components/repl/runMode'
11
-
12
- const ModeWrapper = styled.div`
13
- color: #fff;
14
- `
15
-
16
- const InlineCode = styled.code`
17
- font-size: 0.9em;
18
- background-color: #e4e4e4;
19
- padding: 1px 4px;
20
- border-radius: 2px;
21
- `
22
-
23
- const CODEMIRROR_TSX_MODE: ModeSpec = {
24
- name: 'jsx',
25
- base: {name: 'javascript', typescript: true}
26
- }
27
-
28
- const TSX_MODE_TYPES = ['js', 'jsx', 'tsx', 'ts']
29
-
30
- interface InlineCodeProps {
31
- children: string
32
- }
33
-
34
- interface CodeProps {
35
- children: string
36
- className: 'language-js' | 'language-jsx' | 'language-tsx'
37
- }
38
-
39
- export const components = {
40
- inlineCode: InlineCode,
41
- code: (props: CodeProps) => {
42
- const [lang, range] = parseCodeFenceHeader(
43
- props.className.replace(/^language-/, '')
44
- )
45
- const mode =
46
- lang && TSX_MODE_TYPES.includes(lang) ? CODEMIRROR_TSX_MODE : null
47
- return (
48
- <ModeWrapper>
49
- <CodeMirrorMode highlighted={range} mode={mode}>
50
- {props.children}
51
- </CodeMirrorMode>
52
- </ModeWrapper>
53
- )
54
- }
55
- }
56
-
57
- const hash$ = location$.pipe(map(location => location.hash))
58
-
59
- const BookmarkedLink = rxComponent<{
60
- href: string
61
- children: React.ReactNode
62
- }>(props$ =>
63
- combineLatest([props$, hash$]).pipe(
64
- map(([props, hash]) => (
65
- <a href={props.href} className={hash === props.href ? 'selected' : ''}>
66
- {props.children}
67
- </a>
68
- ))
69
- )
70
- )
71
-
72
- export const tocComponents = {
73
- a: BookmarkedLink
74
- }
@@ -1,134 +0,0 @@
1
- # Reactive Component
2
-
3
- ## component()
4
-
5
- **Signature**
6
-
7
- ```ts
8
- function component<Props>(
9
- observable: Observable<Props>
10
- ): React.FunctionComponent<{}>
11
- function component<Props>(
12
- component: Component<Props>
13
- ): React.FunctionComponent<Props>
14
- ```
15
-
16
- **Usage**
17
-
18
- ```ts
19
- import * as ReactRx from 'react-rx'
20
-
21
- export const MyComponent = ReactRx.component(props$ =>
22
- props$.pipe(map(props => <div>...</div>))
23
- )
24
- ```
25
-
26
- ## forwardRef()
27
-
28
- The `React.forwardRef()` equivalent for a Reactive component.
29
-
30
- ** Signature **
31
-
32
- ```ts
33
- function forwardRef<Props, Ref>(
34
- component: (
35
- input$: Observable<Props>,
36
- ref: React.RefObject<Ref>
37
- ) => React.FunctionComponent<Props>
38
- )
39
- ```
40
-
41
- **Usage**
42
-
43
- ```js
44
- import * as ReactRx from 'react-rx'
45
-
46
- const MyInputWrapper = ReactRx.forwardRef((props$, ref) =>
47
- props$.pipe(
48
- map(props => (
49
- <input
50
- ref={ref}
51
- type="text"
52
- value={props.value}
53
- onChange={props.onChange}
54
- />
55
- ))
56
- )
57
- )
58
- ```
59
-
60
- ## useState()
61
-
62
- Equivalent to `React.useState()` with the only difference that instead of the stateful value, it returns an observable representing the state changes (e.g. instead of `[T, update(value: T)]`, it returns `[Observable<T>, update(value: T)]`.
63
-
64
- **Signature**
65
-
66
- ```ts
67
- function useState<T = undefined>(): [
68
- Observable<T | undefined>,
69
- React.Dispatch<React.SetStateAction<T | undefined>>
70
- ]
71
- function useState<T>(
72
- initial: T | (() => T)
73
- ): [Observable<T>, React.Dispatch<React.SetStateAction<T>>]
74
- ```
75
-
76
- ## useContext()
77
-
78
- Equivalent to `React.useContext()` with the only difference that instead of a stateful context value, it returns an observable representing the context value changes.
79
-
80
- ** Signature **
81
-
82
- ```ts
83
- function useContext<T>(context: React.Context<T>): Observable<T>
84
- ```
85
-
86
- ## useEvent()
87
-
88
- This has no React API equivalent, but creates a `[Observable<Event>, (Event) => void]` tuple, where the first value is an observable of the arguments the second function is called with.
89
-
90
- **Signature**
91
-
92
- ```ts
93
- function useObservableEvent<Event>(): [
94
- Observable<Event>,
95
- (event: Event) => void
96
- ]
97
- ```
98
-
99
- ## toObservable()
100
-
101
- Converts any value inside a Functional React component to an observable of changes to that value.
102
-
103
- ```ts
104
- function toObservable<T>(value: T): Observable<T>
105
- ```
106
-
107
- # React hooks
108
-
109
- ## useObservable()
110
-
111
- A React hook that returns the current/latest value from an observable
112
-
113
- **Signature**
114
-
115
- ```ts
116
- function useObservable<T>(observable$: Observable<T>): T | null
117
- function useObservable<T>(observable$: Observable<T>, initialValue: T): T
118
- ```
119
-
120
- ## useObservableState()
121
-
122
- Same as `useState()` described in the Reactive Component section above, exported under a different name for clarity when using in regular React components
123
-
124
- ## useObservableContext()
125
-
126
- Same as `useContext()` described in the Reactive Component section above, exported under a different name for clarity when using in regular React components
127
-
128
- ## useObservableEvent()
129
-
130
- Same as `useEvent()` described in the Reactive Component section above, exported under a different name for clarity when using in regular React components
131
-
132
- ## toObservable()
133
-
134
- Same as `toObservable()` described in the Reactive Component section above
@@ -1,25 +0,0 @@
1
- import {Header} from '../../components/Header'
2
- import {Container, Content} from '../styles'
3
- import MDXContent, {Toc} from './Api.mdx'
4
- import * as React from 'react'
5
- import {MDXProvider} from '@mdx-js/react'
6
- import {components, tocComponents} from '../../mdx-components'
7
- import {Sidebar} from '../../components/Sidebar'
8
-
9
- export const ApiPage = () => (
10
- <>
11
- <Header />
12
- <Container>
13
- <Sidebar heading="API Reference">
14
- <MDXProvider components={tocComponents}>
15
- <Toc />
16
- </MDXProvider>
17
- </Sidebar>
18
- <Content>
19
- <MDXProvider components={components}>
20
- <MDXContent />
21
- </MDXProvider>
22
- </Content>
23
- </Container>
24
- </>
25
- )
@@ -1,105 +0,0 @@
1
- import * as React from 'react'
2
- import {ContextExample} from '../../examples/context'
3
- import {ErrorsExample} from '../../examples/errors'
4
- import {EventHandlersExample} from '../../examples/event-handlers'
5
- import {FetchExample} from '../../examples/fetch'
6
- import {FormDataExample} from '../../examples/form-data'
7
- import {ForwardRefExample} from '../../examples/forward-ref'
8
- import {PassThroughPropsExample} from '../../examples/passthrough-props'
9
- import {TickExample} from '../../examples/tick'
10
- import {ReactiveStateExample} from '../../examples/reactive-state'
11
- // // import ReadmeExamples from './readme-examples'
12
- import {CodeBlock} from '../../components/repl'
13
- import {FizzBuzzExample} from '../../examples/FizzBuzz'
14
- import {TodoAppExample} from '../../examples/todo-app'
15
- import {HelloWorldExample} from '../../examples/hello-world'
16
- import {SimpleExample} from '../../examples/simple'
17
- import {SharedStateExample} from '../../examples/shared-state'
18
- import {UseElementExample} from '../../examples/use-element'
19
- import {AnimationExample} from '../../examples/animation'
20
- import {SyncExample} from '../../examples/sync'
21
- import {Link} from '../../components/Link'
22
- import {Header} from '../../components/Header'
23
- import {Container, Content} from '../styles'
24
- import {Sidebar} from '../../components/Sidebar'
25
- import {HooksInteropExample} from '../../examples/hooks-interop'
26
- import {UseObservableExample} from '../../examples/use-observable/use-observable'
27
- import {SearchExample} from '../../examples/use-observable/search'
28
-
29
- export interface Example {
30
- id: string
31
- title: string
32
- source: string
33
- description?: string
34
- scope?: {[variableName: string]: any}
35
- prelude?: string
36
- }
37
-
38
- const rxComponentExamples: Example[] = [
39
- HelloWorldExample,
40
- SimpleExample,
41
- ReactiveStateExample,
42
- TodoAppExample,
43
- HooksInteropExample,
44
- AnimationExample,
45
- UseElementExample,
46
- SharedStateExample,
47
- ContextExample,
48
- ErrorsExample,
49
- EventHandlersExample,
50
- FetchExample,
51
- SyncExample,
52
- FormDataExample,
53
- ForwardRefExample,
54
- PassThroughPropsExample,
55
- TickExample,
56
- FizzBuzzExample
57
- ]
58
-
59
- const useObservableExamples: Example[] = [UseObservableExample, SearchExample]
60
-
61
- const allExamples = rxComponentExamples.concat(useObservableExamples)
62
-
63
- export function Examples(props: {selectedExampleName: string}) {
64
- const {selectedExampleName} = props
65
- const selectedExample =
66
- allExamples.find((ex) => ex.id === selectedExampleName) || allExamples[0]
67
-
68
- return (
69
- <>
70
- <Header />
71
- <Container>
72
- <Sidebar heading="Examples">
73
- <ul>
74
- {allExamples.map((ex) => (
75
- <li key={ex.id}>
76
- <Link
77
- className={selectedExampleName === ex.id ? 'selected' : ''}
78
- href={`#${ex.id}`}
79
- >
80
- {ex.title}
81
- </Link>
82
- </li>
83
- ))}
84
- </ul>
85
- </Sidebar>
86
- <Content>
87
- {allExamples
88
- .filter((ex) => ex === selectedExample)
89
- .map((ex) => (
90
- <div key={ex.id}>
91
- <h1>{ex.title}</h1>
92
- {ex.description && <p>{ex.description}</p>}
93
- <CodeBlock
94
- source={ex.source}
95
- scope={ex.scope}
96
- prelude={ex.prelude}
97
- filename={`${ex.id}.tsx`}
98
- />
99
- </div>
100
- ))}
101
- </Content>
102
- </Container>
103
- </>
104
- )
105
- }
@@ -1,13 +0,0 @@
1
- import {rxComponent} from 'react-rx'
2
- import {location$} from '../../datastores/location'
3
- import {map} from 'rxjs/operators'
4
- import {Examples} from './Examples'
5
- import * as React from 'react'
6
-
7
- export const ExamplesPage = rxComponent(
8
- location$.pipe(
9
- map(location => (
10
- <Examples selectedExampleName={location.hash?.substring(1)} />
11
- ))
12
- )
13
- )
@@ -1,275 +0,0 @@
1
- # Reactive components
2
-
3
- ## Intro
4
-
5
- A _reactive component_ is a React component that turns an observable of props into an observable of [values React can render](https://reactjs.org/docs/react-component.html#render) (e.g. element, strings, fragments)
6
-
7
- A _reactive component_ enables you to create components that composes different RxJS streams into a single render function, making your view purely a function of state and providing an explicit dataflow that's easier to reason about.
8
-
9
- The `rxComponent()` utility wraps a function component that is very similar to a regular React function component, with two notable differences:
10
-
11
- 1. Instead of being called with a _props object_, it is called with an _Observable of props_
12
- 2. Instead of returning _renderable values_, it returns an _Observable of renderable values_
13
-
14
- Here's an example component that fetches some text from the url passed as the `url` prop:
15
-
16
- ```jsx
17
- import * as ReactRx from 'react-rx'
18
-
19
- const Fetch = ReactRx.component((props$) =>
20
- props$.pipe(
21
- switchMap((props) => fetch(props.url).then((response) => response.text())),
22
- map((responseText) => <>Response is {responseText}</>)
23
- )
24
- )
25
-
26
- // Usage
27
- ReactDOM.render(<Fetch url="/some/url" />, container)
28
- ```
29
-
30
- Instead of a _function_, `rxComponent` can also take an `Observable` as argument as long as it's an observable of [something React can render](https://reactjs.org/docs/react-component.html#render). This is neat when you have component that doesn't take any props, but still depends on values coming from an `Observable`:
31
-
32
- ```jsx
33
- import * as ReactRx from 'react-rx'
34
-
35
- const Counter = ReactRx.component(
36
- timer(0, 100).pipe(map((counter) => <>The number is {counter}</>))
37
- )
38
-
39
- ReactDOM.render(<Counter />, container)
40
- ```
41
-
42
- ## Local component state
43
-
44
- Instead of calling `React.useState()` directly, you can also use the `useState` function exported from this package. Instead of the actual state value it will return an observable representing the state changes, along with a function to update it. Here's the example above rewritten to use `useState` from this package instead.
45
-
46
- ```jsx{4}
47
- import {rxComponent, useState} from 'react-rx'
48
-
49
- const Counter = rxComponent(() => {
50
- const [count$, setCount] = useState(0)
51
-
52
- return count$.pipe(
53
- map((currentCount) => (
54
- <>
55
- <div>Click count: {currentCount}</div>
56
- <button onClick={() => setCount(currentCount + 1)}>Click!</button>
57
- </>
58
- ))
59
- )
60
- })
61
- ```
62
-
63
- ## Responding to events
64
-
65
- Sometimes your state comes as a function of an event triggered by the user. Let's say you want to display the mouse cursors x,y position as the user moves the mouse inside a component. This can be done by using the `useEvent()` utility:
66
-
67
- ```js{4}
68
- import {useEvent, rxComponent} from 'react-rx'
69
-
70
- const MouseTracker = rxComponent(() => {
71
- const [mouseMove$, onMouseMove] = event()
72
-
73
- return mouseMove$.pipe(
74
- map((event) => (
75
- <>
76
- <div onMouseMove={handleMouseMove}>Hover me!</div>
77
- <pre>
78
- x={event.screenX},y={event.screenY}
79
- </pre>
80
- </>
81
- ))
82
- )
83
- })
84
- ```
85
-
86
- ### Gotcha: Initial render
87
-
88
- The above example will not produce any DOM before a mousemove event has been triggered, and the mouse event can't be triggered before the DOM has been rendered, which puts us in a catch-22 situation. To cope with it, we can make the observable start by emitting an initial value for the event.
89
-
90
- We can do this by using the `startWith` operator that comes with RxJS:
91
-
92
- ```js{8}
93
- import {rxComponent, useEvent} from 'react-rx'
94
- import {map, startWith} from 'rxjs/operators'
95
-
96
- const MouseTracker = rxComponent(() => {
97
- const [mouseMove$, handleMouseMove] = useEvent()
98
-
99
- return mouseMove$.pipe(
100
- startWith(null),
101
- map((event) => (
102
- <>
103
- <div onMouseMove={handleMouseMove}>Hover me!</div>
104
- {event && (
105
- <pre>
106
- x={event.screenX},y={event.screenY}
107
- </pre>
108
- )}
109
- </>
110
- ))
111
- )
112
- })
113
- ```
114
-
115
- This means the `event` argument now can also be `null` so we also need to guard about that in our render function.
116
-
117
- ## Consuming React hooks
118
-
119
- Reactive components will not automatically re-render when a `useState()` value updates. In order to respond to state updates from React hooks, the value needs to be converted to an observable. This is done by wrapping it using the `toObservable()` utility before combining it with the returned observable. Here's an example:
120
-
121
- ```jsx{4}
122
- import {rxComponent} from 'react-rx
123
-
124
- const Counter = rxComponent(props$ => {
125
- const [count, setCount] = React.useState(0)
126
-
127
- return toObservable(count).pipe(
128
- map(currentCount => (
129
- <>
130
- <div>Click count: {currentCount}</div>
131
- <button onClick={() => setCount(currentCount + 1)}>Click!</button>
132
- </>
133
- )))
134
- })
135
- ```
136
-
137
- The `toObservable` function makes it possible to consume community provided React hooks inside a reactive component. However, this package also export some utility functions that wraps regular hooks so you can use them with less boilerplate, for example, the `ReactRx.useState` function described above.
138
-
139
- ### React ecosystem interop
140
-
141
- Using `reactiveComponent` in a project with regular react components or components installed from the React ecosystem just works. Although it's advisable to not consume React hooks inside a `reactiveComponent`, but it's still possible by turning a value into an observable with `useAsObservable`. Here's an example:
142
-
143
- ```tsx
144
- import {useAsObservable, rxComponent} from 'react-rx
145
-
146
- function useClickCounter(): [number, () => void] {
147
- const [clickCount, setClickCount] = React.useState(0)
148
- const inc = React.useCallback(() => setClickCount((c) => c + 1), [])
149
-
150
- return [clickCount, inc]
151
- }
152
-
153
- const HooksInteropExample = rxComponent(() => {
154
- const [clickCount, inc] = useClickCounter()
155
-
156
- // This is required in order to turn clickCount into an observable value
157
- const click$ = useAsObservable(clickCount)
158
-
159
- return click$.pipe(
160
- map((clickCount) => (
161
- <button onClick={inc}>
162
- This button has been clicked {clickCount} times
163
- </button>
164
- ))
165
- )
166
- })
167
- ```
168
-
169
- # Observable Hooks
170
-
171
- ## useObservable()
172
-
173
- Use observables in React components with the `useObservable` hook.
174
-
175
- If you need to subscribe to an observable in your component, this hook will give you the current value from it
176
-
177
- Example:
178
-
179
- ```jsx
180
- import {useObservable} from 'react-rx'
181
- import {interval} from 'rxjs'
182
-
183
- function MyComponent(props) {
184
- const number = useObservable(interval(100), 0)
185
-
186
- return <>The number is {number}</>
187
- }
188
- ```
189
-
190
- The `initialValue` argument is optional. If its omitted, the value returned from `useObservable` may be `null` initially. If the observable emits a value _synchronously_ at subscription time, that value will be used as the initial value, and any `initialValue` passed as argument to `useObservable` will be ignored:
191
-
192
- ```jsx
193
- import {of} from 'rxjs'
194
- import {useObservable} from 'react-rx'
195
-
196
- // This component will never render "Hello mars!" since the observable emits "world" synchronously.
197
- function MyComponent(props) {
198
- const planet = useObservable(of('world'), 'mars')
199
-
200
- return <>Hello {planet}!</>
201
- }
202
- ```
203
-
204
- ## useObservableState()
205
-
206
- If you need to represent some piece of state as an observable and also want the ability to change this state during the lifetime of the component, `useObservableState` is for you. It acts like `React.useState()`, only that it returns an observable representing changes to the value instead of the value itself. The callback/setter returned acts like a the regular callback you would otherwise get from `React.useState`. This is useful when you want to compose the state change together with other observables.
207
-
208
- Note: this is exact same hook as the `useState()` function described in the section about Reactive Component above, it's only been exported under a different and more explicit name to distinguish better from `React.useState()`.
209
-
210
- Here's an example of a component that count numbers at a certain speed, allowing the user to adjust the counting speed by adjusting a slider:
211
-
212
- ```jsx
213
- import {useObservableState} from 'react-rx'
214
-
215
- function MyComponent(props) {
216
- const [speed$, setSpeed] = useObservableState(1)
217
-
218
- const count$ = speed$.pipe(
219
- switchMap((speed) => timer(0, 1000 / speed)),
220
- scan((count) => count + 1, 0)
221
- )
222
-
223
- const currentSpeed = useObservable(speed$)
224
- const currentCount = useObservable(count$)
225
-
226
- return (
227
- <div>
228
- <div>Counter value: {currentCount}</div>
229
- <div>Counting speed: {Math.round((1 / currentSpeed) * 100) / 100}s</div>
230
- <input
231
- type="range"
232
- value={currentSpeed}
233
- min={1}
234
- max={10}
235
- onChange={(event) => setSpeed(Number(event.currentTarget.value))}
236
- />
237
- </div>
238
- )
239
- }
240
- ```
241
-
242
- ## useObservableEvent()
243
-
244
- This creates an event handler and an observable that represents calls to that handler.
245
-
246
- Here's an example of a component thad displays the current value from a range input:
247
-
248
- ```jsx
249
- import {useObservableEvent} from 'react-rx'
250
-
251
- const ShowSliderValue = () => {
252
- const [onSliderChange$, onSliderChange] = useObservableEvent()
253
-
254
- const sliderValue = useObservable(
255
- onSliderChange$.pipe(
256
- map((event) => event.currentTarget.value),
257
- map((value) => Number(value)),
258
- startWith(1)
259
- )
260
- )
261
-
262
- return (
263
- <>
264
- <input
265
- type="range"
266
- value={sliderValue}
267
- min={1}
268
- max={10}
269
- onChange={onSliderChange}
270
- />
271
- <div>Value is: {sliderValue}</div>
272
- </>
273
- )
274
- }
275
- ```
@@ -1,25 +0,0 @@
1
- import {Header} from '../../components/Header'
2
- import MDXContent, {Toc} from './Guide.mdx'
3
- import * as React from 'react'
4
- import {MDXProvider} from '@mdx-js/react'
5
- import {components, tocComponents} from '../../mdx-components'
6
- import {Container, Content} from '../styles'
7
- import {Sidebar} from '../../components/Sidebar'
8
-
9
- export const GuidePage = () => (
10
- <>
11
- <Header />
12
- <Container>
13
- <Sidebar heading="Guide">
14
- <MDXProvider components={tocComponents}>
15
- <Toc />
16
- </MDXProvider>
17
- </Sidebar>
18
- <Content>
19
- <MDXProvider components={components}>
20
- <MDXContent />
21
- </MDXProvider>
22
- </Content>
23
- </Container>
24
- </>
25
- )