-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(react): add useState React hook
- Loading branch information
Showing
4 changed files
with
186 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
import test from 'ava'; | ||
import { renderHook } from '@testing-library/react-hooks'; | ||
import { useStream } from './useStream'; | ||
import { empty, Subject } from 'rxjs'; | ||
|
||
const initial = 'x'; | ||
const second = 'A'; | ||
|
||
test('useStream return initial value right away', t => { | ||
const { result } = renderHook(() => useStream(empty(), initial)); | ||
|
||
t.deepEqual(result.current, 'x'); | ||
}); | ||
|
||
test('useStream return value from stream', t => { | ||
const source$ = new Subject<string>(); | ||
const { result } = renderHook(() => useStream(source$, initial)); | ||
|
||
source$.next(second); | ||
|
||
t.deepEqual(result.current, second); | ||
}); | ||
|
||
test('useStream unsubscribes on unmount', t => { | ||
const source$ = new Subject<string>(); | ||
const { unmount } = renderHook(() => useStream(source$, initial)); | ||
|
||
unmount(); | ||
|
||
t.deepEqual(source$.observers, []); | ||
}); | ||
|
||
test('useStream does not resubscribe on rerender', t => { | ||
const source$ = new Subject<string>(); | ||
const { rerender } = renderHook(() => useStream(source$, initial)); | ||
|
||
const subscription = source$.observers[0]; | ||
rerender(); | ||
|
||
t.assert(source$.observers[0] === subscription); | ||
}); | ||
|
||
test('useStream does not update for changed initial value', t => { | ||
const source$ = new Subject<string>(); | ||
const { rerender, result } = renderHook( | ||
props => useStream(source$, props.initial), | ||
{ | ||
initialProps: { initial }, | ||
} | ||
); | ||
|
||
source$.next(second); | ||
rerender({ initial: 'something' }); | ||
|
||
t.deepEqual(result.current, second); | ||
}); | ||
|
||
test('useStream unsubscribes, keeps latest value and subscribes new stream', t => { | ||
const source1$ = new Subject<string>(); | ||
const source2$ = new Subject<string>(); | ||
|
||
const { rerender, result } = renderHook( | ||
props => useStream(props.source$, initial), | ||
{ | ||
initialProps: { source$: source1$ }, | ||
} | ||
); | ||
|
||
source1$.next(second); | ||
rerender({ source$: source2$ }); | ||
|
||
t.deepEqual(result.current, second); | ||
t.deepEqual(source1$.observers, []); | ||
t.deepEqual(source2$.observers.length, 1); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
import { useState, useEffect } from 'react'; | ||
import { Observable } from 'rxjs'; | ||
|
||
/** | ||
* React hook to subscribe to a stream | ||
* | ||
* Each emit from the stream will make the component re-render with the new | ||
* value. Initially the `initial` argument is returned, because an `Observable` | ||
* has no guarantee for when the first emit will happen. | ||
* | ||
* This hook passes the `source$` argument as a dependency to `useEffect`, which | ||
* means you will need to take care that it is referentially equal between each | ||
* render (unless you want to resubscribe, of course). Generally, you should | ||
* only use this hook for static/global streams. | ||
* | ||
* @param source$ Stream that provides the needed values | ||
* @param initial Initial value to return | ||
* @see useEffect | ||
*/ | ||
export const useStream = <T, I>(source$: Observable<T>, initial: I): T | I => { | ||
const [value, setValue] = useState<T | I>(initial); | ||
|
||
useEffect(() => { | ||
const subscription = source$.subscribe(setValue); | ||
|
||
return () => subscription.unsubscribe(); | ||
}, [source$]); | ||
|
||
return value; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -115,7 +115,7 @@ | |
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.7.7.tgz#1b886595419cf92d811316d5b715a53ff38b4937" | ||
integrity sha512-WtTZMZAZLbeymhkd/sEaPD8IQyGAhmuTuvTzLiCFM7iXiVdY0gc0IaI+cW0fh1BnSMbJSzXX6/fHllgHKwHhXw== | ||
|
||
"@babel/runtime@^7.6.3": | ||
"@babel/runtime@^7.5.4", "@babel/runtime@^7.6.3": | ||
version "7.8.7" | ||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.8.7.tgz#8fefce9802db54881ba59f90bb28719b4996324d" | ||
integrity sha512-+AATMUFppJDw6aiR5NVPHqIQBlV/Pj8wY/EZH+lmvRdUo9xBaz/rF3alAwFJQavvKfeOlPE7oaaDHVbcySbCsg== | ||
|
@@ -409,6 +409,14 @@ | |
dependencies: | ||
defer-to-connect "^1.0.1" | ||
|
||
"@testing-library/react-hooks@^3.2.1": | ||
version "3.2.1" | ||
resolved "https://registry.yarnpkg.com/@testing-library/react-hooks/-/react-hooks-3.2.1.tgz#19b6caa048ef15faa69d439c469033873ea01294" | ||
integrity sha512-1OB6Ksvlk6BCJA1xpj8/WWz0XVd1qRcgqdaFAq+xeC6l61Ucj0P6QpA5u+Db/x9gU4DCX8ziR5b66Mlfg0M2RA== | ||
dependencies: | ||
"@babel/runtime" "^7.5.4" | ||
"@types/testing-library__react-hooks" "^3.0.0" | ||
|
||
"@types/circular-json@^0.4.0": | ||
version "0.4.0" | ||
resolved "https://registry.yarnpkg.com/@types/circular-json/-/circular-json-0.4.0.tgz#7401f7e218cfe87ad4c43690da5658b9acaf51be" | ||
|
@@ -463,6 +471,26 @@ | |
resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" | ||
integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== | ||
|
||
"@types/prop-types@*": | ||
version "15.7.3" | ||
resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.3.tgz#2ab0d5da2e5815f94b0b9d4b95d1e5f243ab2ca7" | ||
integrity sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw== | ||
|
||
"@types/react-test-renderer@*": | ||
version "16.9.2" | ||
resolved "https://registry.yarnpkg.com/@types/react-test-renderer/-/react-test-renderer-16.9.2.tgz#e1c408831e8183e5ad748fdece02214a7c2ab6c5" | ||
integrity sha512-4eJr1JFLIAlWhzDkBCkhrOIWOvOxcCAfQh+jiKg7l/nNZcCIL2MHl2dZhogIFKyHzedVWHaVP1Yydq/Ruu4agw== | ||
dependencies: | ||
"@types/react" "*" | ||
|
||
"@types/react@*", "@types/[email protected]": | ||
version "16.9.23" | ||
resolved "https://registry.yarnpkg.com/@types/react/-/react-16.9.23.tgz#1a66c6d468ba11a8943ad958a8cb3e737568271c" | ||
integrity sha512-SsGVT4E7L2wLN3tPYLiF20hmZTPGuzaayVunfgXzUn1x4uHVsKH6QDJQ/TdpHqwsTLd4CwrmQ2vOgxN7gE24gw== | ||
dependencies: | ||
"@types/prop-types" "*" | ||
csstype "^2.2.0" | ||
|
||
"@types/[email protected]": | ||
version "7.5.2" | ||
resolved "https://registry.yarnpkg.com/@types/sinon/-/sinon-7.5.2.tgz#5e2f1d120f07b9cda07e5dedd4f3bf8888fccdb9" | ||
|
@@ -473,6 +501,14 @@ | |
resolved "https://registry.yarnpkg.com/@types/stacktrace-js/-/stacktrace-js-0.0.32.tgz#d23e4a36a5073d39487fbea8234cc6186862d389" | ||
integrity sha512-SdxmlrHfO0BxgbBP9HZWMUo2rima8lwMjPiWm6S0dyKkDa5CseamktFhXg8umu3TPVBkSlX6ZoB5uUDJK89yvg== | ||
|
||
"@types/testing-library__react-hooks@^3.0.0": | ||
version "3.2.0" | ||
resolved "https://registry.yarnpkg.com/@types/testing-library__react-hooks/-/testing-library__react-hooks-3.2.0.tgz#52f3a109bef06080e3b1e3ae7ea1c014ce859897" | ||
integrity sha512-dE8iMTuR5lzB+MqnxlzORlXzXyCL0EKfzH0w/lau20OpkHD37EaWjZDz0iNG8b71iEtxT4XKGmSKAGVEqk46mw== | ||
dependencies: | ||
"@types/react" "*" | ||
"@types/react-test-renderer" "*" | ||
|
||
"@typescript-eslint/eslint-plugin@^2.18.0": | ||
version "2.18.0" | ||
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.18.0.tgz#f8cf272dfb057ecf1ea000fea1e0b3f06a32f9cb" | ||
|
@@ -1384,6 +1420,11 @@ crypto-random-string@^2.0.0: | |
resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-2.0.0.tgz#ef2a7a966ec11083388369baa02ebead229b30d5" | ||
integrity sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA== | ||
|
||
csstype@^2.2.0: | ||
version "2.6.9" | ||
resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.9.tgz#05141d0cd557a56b8891394c1911c40c8a98d098" | ||
integrity sha512-xz39Sb4+OaTsULgUERcCk+TJj8ylkL4aSVDQiX/ksxbELSqwkgt4d4RD7fovIdgJGSuNYqwZEiVjYY5l0ask+Q== | ||
|
||
currently-unhandled@^0.4.1: | ||
version "0.4.1" | ||
resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea" | ||
|
@@ -2883,7 +2924,7 @@ log-symbols@^3.0.0: | |
dependencies: | ||
chalk "^2.4.2" | ||
|
||
loose-envify@^1.4.0: | ||
loose-envify@^1.1.0, loose-envify@^1.4.0: | ||
version "1.4.0" | ||
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" | ||
integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== | ||
|
@@ -3598,7 +3639,7 @@ progress@^2.0.0: | |
resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" | ||
integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== | ||
|
||
prop-types@^15.7.2: | ||
prop-types@^15.6.2, prop-types@^15.7.2: | ||
version "15.7.2" | ||
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5" | ||
integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ== | ||
|
@@ -3652,6 +3693,30 @@ react-is@^16.8.1: | |
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.12.0.tgz#2cc0fe0fba742d97fd527c42a13bec4eeb06241c" | ||
integrity sha512-rPCkf/mWBtKc97aLL9/txD8DZdemK0vkA3JMLShjlJB3Pj3s+lpf1KaBzMfQrAmhMQB0n1cU/SUGgKKBCe837Q== | ||
|
||
react-is@^16.8.6: | ||
version "16.13.0" | ||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.0.tgz#0f37c3613c34fe6b37cd7f763a0d6293ab15c527" | ||
integrity sha512-GFMtL0vHkiBv9HluwNZTggSn/sCyEt9n02aM0dSAjGGyqyNlAyftYm4phPxdvCigG15JreC5biwxCgTAJZ7yAA== | ||
|
||
[email protected]: | ||
version "16.13.0" | ||
resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-16.13.0.tgz#39ba3bf72cedc8210c3f81983f0bb061b14a3014" | ||
integrity sha512-NQ2S9gdMUa7rgPGpKGyMcwl1d6D9MCF0lftdI3kts6kkiX+qvpC955jNjAZXlIDTjnN9jwFI8A8XhRh/9v0spA== | ||
dependencies: | ||
object-assign "^4.1.1" | ||
prop-types "^15.6.2" | ||
react-is "^16.8.6" | ||
scheduler "^0.19.0" | ||
|
||
[email protected]: | ||
version "16.13.0" | ||
resolved "https://registry.yarnpkg.com/react/-/react-16.13.0.tgz#d046eabcdf64e457bbeed1e792e235e1b9934cf7" | ||
integrity sha512-TSavZz2iSLkq5/oiE7gnFzmURKZMltmi193rm5HEoUDAXpzT9Kzw6oNZnGoai/4+fUnm7FqS5dwgUL34TujcWQ== | ||
dependencies: | ||
loose-envify "^1.1.0" | ||
object-assign "^4.1.1" | ||
prop-types "^15.6.2" | ||
|
||
read-pkg-up@^1.0.1: | ||
version "1.0.1" | ||
resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02" | ||
|
@@ -3965,6 +4030,14 @@ safe-buffer@~5.1.0, safe-buffer@~5.1.1: | |
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" | ||
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== | ||
|
||
scheduler@^0.19.0: | ||
version "0.19.0" | ||
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.19.0.tgz#a715d56302de403df742f4a9be11975b32f5698d" | ||
integrity sha512-xowbVaTPe9r7y7RUejcK73/j8tt2jfiyTednOvHbA8JoClvMYCp+r8QegLwK/n8zWQAtZb1fFnER4XLBZXrCxA== | ||
dependencies: | ||
loose-envify "^1.1.0" | ||
object-assign "^4.1.1" | ||
|
||
semver-compare@^1.0.0: | ||
version "1.0.0" | ||
resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc" | ||
|