diff --git a/.all-contributorsrc b/.all-contributorsrc
index 16192f16..e37950f0 100644
--- a/.all-contributorsrc
+++ b/.all-contributorsrc
@@ -3,9 +3,7 @@
"projectOwner": "romac",
"repoType": "github",
"repoHost": "https://github.com",
- "files": [
- "README.md"
- ],
+ "files": ["README.md"],
"imageSize": 100,
"commit": true,
"commitConvention": "angular",
@@ -15,189 +13,133 @@
"name": "Romain Ruetschi",
"avatar_url": "https://avatars2.githubusercontent.com/u/106849?v=4",
"profile": "https://romac.me/",
- "contributions": [
- "code",
- "doc",
- "example",
- "ideas",
- "infra",
- "maintenance",
- "projectManagement",
- "review",
- "question",
- "test"
- ]
+ "contributions": ["code", "doc", "example", "ideas", "infra", "maintenance", "projectManagement", "review", "question", "test"]
},
{
"login": "Favna",
"name": "Jeroen Claassens",
"avatar_url": "https://avatars3.githubusercontent.com/u/4019718?v=4",
"profile": "https://favware.tech/",
- "contributions": [
- "code",
- "doc",
- "example",
- "infra",
- "maintenance",
- "projectManagement",
- "review",
- "question",
- "test"
- ]
+ "contributions": ["code", "doc", "example", "infra", "maintenance", "projectManagement", "review", "question", "test"]
},
{
"login": "cyrilchapon",
"name": "Cyril CHAPON",
"avatar_url": "https://avatars1.githubusercontent.com/u/10728426?v=4",
"profile": "https://github.com/cyrilchapon",
- "contributions": [
- "code",
- "doc",
- "test"
- ]
+ "contributions": ["code", "doc", "test"]
},
{
"login": "timjacobi",
"name": "Tim Jacobi",
"avatar_url": "https://avatars2.githubusercontent.com/u/2023165?v=4",
"profile": "http://twitter.com/tim_jacobi",
- "contributions": [
- "code",
- "doc",
- "test"
- ]
+ "contributions": ["code", "doc", "test"]
},
{
"login": "meszaros-lajos-gyorgy",
"name": "Lajos György Mészáros",
"avatar_url": "https://avatars3.githubusercontent.com/u/2386064?v=4",
"profile": "https://github.com/meszaros-lajos-gyorgy",
- "contributions": [
- "code",
- "doc"
- ]
+ "contributions": ["code", "doc"]
},
{
"login": "Rleahy22",
"name": "Ryan Leahy",
"avatar_url": "https://avatars2.githubusercontent.com/u/3144003?v=4",
"profile": "https://github.com/Rleahy22",
- "contributions": [
- "code",
- "doc"
- ]
+ "contributions": ["code", "doc"]
},
{
"login": "pronebird",
"name": "Andrej Mihajlov",
"avatar_url": "https://avatars2.githubusercontent.com/u/704044?v=4",
"profile": "https://github.com/pronebird",
- "contributions": [
- "platform",
- "code"
- ]
+ "contributions": ["platform", "code"]
},
{
"login": "ejbp",
"name": "While True",
"avatar_url": "https://avatars0.githubusercontent.com/u/2060105?v=4",
"profile": "https://github.com/ejbp",
- "contributions": [
- "bug",
- "code"
- ]
+ "contributions": ["bug", "code"]
},
{
"login": "TeffenEllis",
"name": "Teffen Ellis",
"avatar_url": "https://avatars3.githubusercontent.com/u/592134?v=4",
"profile": "https://nirri.us/",
- "contributions": [
- "projectManagement",
- "code"
- ]
+ "contributions": ["projectManagement", "code"]
},
{
"login": "Mamoru1234",
"name": "Alexei Gontar",
"avatar_url": "https://avatars2.githubusercontent.com/u/11805198?v=4",
"profile": "https://github.com/Mamoru1234",
- "contributions": [
- "code"
- ]
+ "contributions": ["code"]
},
{
"login": "melyourhero",
"name": "Dmitry Melnik",
"avatar_url": "https://avatars3.githubusercontent.com/u/11873817?v=4",
"profile": "https://github.com/melyourhero",
- "contributions": [
- "code"
- ]
+ "contributions": ["code"]
},
{
"login": "eropple",
"name": "Ed Ropple",
"avatar_url": "https://avatars2.githubusercontent.com/u/109262?v=4",
"profile": "https://github.com/eropple",
- "contributions": [
- "code"
- ]
+ "contributions": ["code"]
},
{
"login": "gradosevic",
"name": "Goran Radosevic",
"avatar_url": "https://avatars2.githubusercontent.com/u/10562516?v=4",
"profile": "https://github.com/gradosevic",
- "contributions": [
- "doc"
- ]
+ "contributions": ["doc"]
},
{
"login": "Haroenv",
"name": "Haroen Viaene",
"avatar_url": "https://avatars3.githubusercontent.com/u/6270048?v=4",
"profile": "https://haroen.me/",
- "contributions": [
- "doc"
- ]
+ "contributions": ["doc"]
},
{
"login": "martijnthe",
"name": "Martijn Thé",
"avatar_url": "https://avatars3.githubusercontent.com/u/193881?v=4",
"profile": "http://www.martijnthe.nl/",
- "contributions": [
- "code"
- ]
+ "contributions": ["code"]
},
{
"login": "artcoding-git",
"name": "artcoding-git",
"avatar_url": "https://avatars0.githubusercontent.com/u/20770507?v=4",
"profile": "https://github.com/artcoding-git",
- "contributions": [
- "doc"
- ]
+ "contributions": ["doc"]
},
{
"login": "Ako520",
"name": "王天博",
"avatar_url": "https://avatars3.githubusercontent.com/u/22900569?v=4",
"profile": "https://github.com/Ako520",
- "contributions": [
- "bug",
- "code"
- ]
+ "contributions": ["bug", "code"]
},
{
"login": "Arcath",
"name": "Adam Laycock",
"avatar_url": "https://avatars1.githubusercontent.com/u/19609?v=4",
"profile": "https://arcath.net/",
- "contributions": [
- "code"
- ]
+ "contributions": ["code"]
+ },
+ {
+ "login": "theo-js",
+ "name": "Theo B.",
+ "avatar_url": "https://avatars.githubusercontent.com/u/58733465?v=4",
+ "profile": "https://github.com/theo-js",
+ "contributions": ["code"]
}
],
"contributorsPerLine": 7
diff --git a/.gitignore b/.gitignore
index 1c34b3d5..47408269 100644
--- a/.gitignore
+++ b/.gitignore
@@ -15,7 +15,7 @@ coverage/
# Ignore output folders
dist/
docs/
-.cache/
+.parcel-cache/
# Ignore JavaScript files
**/*.js
@@ -26,6 +26,7 @@ docs/
**/*.tsbuildinfo
!jest.config.js
!.prettierrc.js
+!demo/tailwind.config.js
# Ignore heapsnapshot and log files
*.heapsnapshot
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 426bf680..19c5317e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,27 +4,25 @@ All notable changes to this project will be documented in this file. See [standa
### [4.0.1](https://github.com/romac/react-if/compare/v4.0.0...v4.0.1) (2020-10-21)
-
### ⚠ BREAKING CHANGES
-* **readme:** As part of the rewrite, the global exports are gone.
-Please use CommonJS or ESM style imports as specified in the README.
+- **readme:** As part of the rewrite, the global exports are gone.
+ Please use CommonJS or ESM style imports as specified in the README.
### Bug Fixes
-* **readme:** update shields ([d975fd4](https://github.com/romac/react-if/commit/d975fd44b737caff913725905abe316a02097236))
+- **readme:** update shields ([d975fd4](https://github.com/romac/react-if/commit/d975fd44b737caff913725905abe316a02097236))
## [4.0.0](https://github.com/romac/react-if/compare/v3.4.3...v4.0.0) (2020-10-21)
-
### ⚠ BREAKING CHANGES
-* As part of the rewrite, the global exports are gone. Please use CommonJS or ESM
-style imports as specified in the README.
+- As part of the rewrite, the global exports are gone. Please use CommonJS or ESM
+ style imports as specified in the README.
### Features
-* rewrite entire lib from scratch ([9f93221](https://github.com/romac/react-if/commit/9f93221999e23bc39db98575aa72e245935ccb6d)), closes [#55](https://github.com/romac/react-if/issues/55) [#57](https://github.com/romac/react-if/issues/57)
+- rewrite entire lib from scratch ([9f93221](https://github.com/romac/react-if/commit/9f93221999e23bc39db98575aa72e245935ccb6d)), closes [#55](https://github.com/romac/react-if/issues/55) [#57](https://github.com/romac/react-if/issues/57)
## 3.4.3 (2019-06-17)
diff --git a/README.md b/README.md
index 0f4d5c2d..5925676d 100644
--- a/README.md
+++ b/README.md
@@ -102,7 +102,7 @@ const { If, Then, Else, When, Unless, Switch, Case, Default } = require('react-i
## Examples
-## Swich/Case/Default
+## Switch/Case/Default
```jsx
import React from 'react';
@@ -142,33 +142,80 @@ const AnotherExample = () => (
);
```
+## Asynchronous condition
+
+```jsx
+import React from 'react';
+import { If, Fallback, Then, Else } from 'react-if';
+
+const Example = () => {
+ const fetchData = () => {
+ // Return promise
+ };
+
+ return (
+
+
+ Loading data ...
+
+ {(data) => (
+ Here is your data: {data}
+ )}
+
+
+ {(error) => (
+ Failed to load data because "{error}"
+ )}
+
+
+
+ );
+});
+```
+
## API
**_Note: For a fully auto-generated API, see [the github pages website](https://romac.github.io/react-if)_**
-### <If />
+### ;
-| Property | Type |
-| ----------- | ------- |
-| `condition` | Boolean |
+| Property | Type | Default |
+| ----------- | --------------- | ------- |
+| `condition` | Boolean/Promise | |
+| `keepAlive` | Boolean | false |
If `condition` evaluates to `true`, renders the ` ` block will be rendered, otherwise renders the ` ` block. Either block may be omitted.
This component can contain any number of ` ` or ` ` blocks, but only the first block of the right type (either `Then` or `Else`, depending on the condition) will be rendered.
-### <Then />
+When passing a Promise to `condition`, renders the `Fallback` block while the Promise is pending, the ` ` block once Promise is resolved, and the ` ` block when Promise is rejected.
+The return value of the `Promise` can be retrieved within the ` ` and ` ` blocks; a render function must be child of these blocks.
+
+```jsx
+{(returnValue, promiseHistory, cancellablePromise) => {returnValue} }
+```
+
+The parameters of this render function are:
+
+- `returnValue`: The return value of the `Promise` (for the ` ` block) or the error (for the ` ` block);
+- `promiseHistory`: an Array of all the Promises that were ever passed to ` `. It contains cancellablePromise Objects, that have a promise, as well as a `cancel` method used to cancel the promise;
+- `cancellablePromise`: the cancellablePromise Object containing the promise that caused the rendering of this ` | ` block;
+
+If the `keepAlive` prop evaluates to `false`, each rerender of the ` ` component will automatically ignore the previous Promise if it was still pending.
+
+### ;
Can contain any number of elements inside, which it renders as-is. It can also contain a function. Should not be used outside of an ` ` block. It will only be displayed, if parent `If` block's condition is true.
-### <Else />
+### ;
Can contain any number of elements inside, which it renders as-is. It can also contain a function. Should not be used outside of an ` ` block. It will only be displayed, if parent `If` block's condition is false.
-### <Switch />
+### ;
A container for `` and ` ` blocks. It will render **the first matching** `Case`, or **the first encountered** `Default` (, or null).
-### <Case />
+### ;
| Property | Type |
| ----------- | ------- |
@@ -176,15 +223,15 @@ A container for `` and ` ` blocks. It will rend
If the `Case` is the first one to have its `condition` evaluates to `true` inside the parent ` ` it will be the only rendered.
-### <Default />
+### ;
If no `Case` have its `condition` evaluates to `true` inside the parent ` `, the first `Default` will be the only one rendered.
-### <When />
+### ;
A shorthand for `... `. The same rules apply to the child elements as with using the `Then` block.
-### <Unless />
+### ;
A shorthand for `... `. The same rules apply to the child elements as with using the `Else` block.
@@ -223,6 +270,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
artcoding-git 📖
王天博 🐛 💻
Adam Laycock 💻
+ Theo B. 💻
diff --git a/demo/.postcssrc b/demo/.postcssrc
new file mode 100644
index 00000000..5622d47b
--- /dev/null
+++ b/demo/.postcssrc
@@ -0,0 +1,6 @@
+{
+ "plugins": {
+ "tailwindcss": {},
+ "autoprefixer": {}
+ }
+}
\ No newline at end of file
diff --git a/demo/README.md b/demo/README.md
index b3b415e1..df8e467e 100644
--- a/demo/README.md
+++ b/demo/README.md
@@ -6,4 +6,4 @@ To run this demo application:
1. Run `yarn start` in the demo directory
- This will also build the source code in the root directory
-2. Parcel will now start a dev session and you can open it in your browser (by default on `localhost:1234`)
\ No newline at end of file
+2. Parcel will now start a dev session and you can open it in your browser (by default on `localhost:1234`)
diff --git a/demo/components/IfWithPromise.tsx b/demo/components/IfWithPromise.tsx
new file mode 100644
index 00000000..4d387eb7
--- /dev/null
+++ b/demo/components/IfWithPromise.tsx
@@ -0,0 +1,139 @@
+import * as React from 'react';
+import { Else, Fallback, If, Then } from '../../dist';
+
+interface ExtendedPromise extends Promise {
+ _id?: number;
+ fulfilled?: boolean;
+}
+
+interface Props {
+ resolutionReturnValue: unknown;
+ rejectionReturnValue: unknown;
+ delay: number;
+}
+
+const IfWithPromise: React.FC = ({ resolutionReturnValue, rejectionReturnValue, delay }) => {
+ const [currentPromise, setCurrentPromise] = React.useState(null);
+ const currentPromiseId = React.useRef(1);
+ const [keepAlive, setKeepAlive] = React.useState(false);
+ const [createdPromises, setCreatedPromises] = React.useState>([]);
+
+ const pendingPromises = React.useMemo((): ExtendedPromise[] => createdPromises.filter((p) => !p.fulfilled), [createdPromises]);
+ const fulfilledPromises = React.useMemo((): ExtendedPromise[] => createdPromises.filter((p) => p.fulfilled), [createdPromises]);
+
+ const createPromise = (shouldResolve: boolean) => {
+ const newPromise: ExtendedPromise = new Promise((resolve, reject) => {
+ setTimeout(() => {
+ shouldResolve ? resolve(resolutionReturnValue) : reject(rejectionReturnValue);
+ }, delay * 1000);
+ });
+ newPromise._id = currentPromiseId.current;
+ currentPromiseId.current++;
+ newPromise.fulfilled = false;
+
+ setCurrentPromise(newPromise);
+ setCreatedPromises([...createdPromises, newPromise]);
+ };
+
+ const markAsFulfilled = React.useCallback(
+ (fulfilledCancellablePromises: Array) => {
+ setCreatedPromises(
+ createdPromises.map((createdPromise) => {
+ const isInHistoryOfFulfilled = fulfilledCancellablePromises.find(
+ (cancellablePromise) => cancellablePromise.promise._id === createdPromise._id
+ );
+ if (Boolean(isInHistoryOfFulfilled)) {
+ createdPromise.fulfilled = true;
+ }
+ return createdPromise;
+ })
+ );
+ },
+ [createdPromises]
+ );
+
+ const ifBlock = React.useMemo(
+ (): React.ReactNode => (
+
+
+ {(data: any, history: Array) => {
+ // FOR DEMO PURPOSE ONLY; DO NOT SET STATE INSIDE RENDER OTHERWISE
+ setTimeout(() => markAsFulfilled(history));
+
+ return (
+
+ This {' '} block shows up once promise is resolved; Return value of promise resolution:
+ {data || toString.call(data)}
+
+ );
+ }}
+
+
+ {(error: any, history: Array) => {
+ setTimeout(() => markAsFulfilled(history));
+ return (
+
+ This {' '} block shows up only if promise is rejected; Error thrown by promise rejection:
+ {error || toString.call(error)}
+
+ );
+ }}
+
+
+
+ This is the {' '} block; waiting for the promise to
+ be fulfilled (after {delay} seconds) ...
+
+
+
+ ),
+ [currentPromise, delay, keepAlive, markAsFulfilled]
+ );
+
+ return (
+
+ {currentPromise && ifBlock}
+
+
+ createPromise(true)} className="btn btn-accent mr-2">
+ Create promise and resolve
+
+ createPromise(false)} className="btn btn-secondary">
+ Create promise and reject
+
+
+
+
+
+ keepAlive:
+ setKeepAlive(!keepAlive)} id="keep-alive" className="checkbox">
+
+
+
+ {currentPromise && (
+
+
+ Pending promises: {pendingPromises.length}
+
+
+ Fulfilled promises: {fulfilledPromises.length}
+
+
+ Total: {createdPromises.length}
+
+
+ )}
+ {keepAlive && (
+
+
+ Alternate clicks on each button to observe the effect of the keepAlive prop
+
+
+ )}
+
+
+
+ );
+};
+
+export default IfWithPromise;
diff --git a/demo/index.css b/demo/index.css
new file mode 100644
index 00000000..7b6d7e2a
--- /dev/null
+++ b/demo/index.css
@@ -0,0 +1,52 @@
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
+
+body,
+html {
+ margin: 0;
+ padding: 0;
+}
+
+* {
+ box-sizing: border-box;
+}
+
+html,
+body {
+ display: grid;
+ place-content: center;
+ height: 100vh;
+ width: 100vw;
+ font-family: BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
+ font-size: large;
+}
+
+.inline-spinner {
+ display: inline-block;
+ height: 0.66em;
+ width: 0.66em;
+ background: #0000;
+ border-radius: 50%;
+ border: 0.125em solid cyan;
+ border-right-color: #0000;
+ animation: rotate 0.66s linear infinite;
+}
+
+@keyframes rotate {
+ from {
+ transform: rotate(0deg);
+ }
+ to {
+ transform: rotate(359deg);
+ }
+}
+
+@keyframes stretch-w {
+ from {
+ width: 0;
+ }
+ to {
+ width: 100%;
+ }
+}
diff --git a/demo/index.html b/demo/index.html
index 194f65e6..3eb5a368 100644
--- a/demo/index.html
+++ b/demo/index.html
@@ -5,27 +5,10 @@
Playground
-
-
-
-
+
+