diff --git a/README.md b/README.md index 62914b9..30aab76 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,6 @@ Bring back previously-unmounted components and children -- state and all -- when they remount. -**These packages are in active development. Things will change rapidly, and it is not yet production-ready. Feedback is welcome.** - [![build status](https://img.shields.io/travis/com/spautz/react-hibernate/master.svg)](https://travis-ci.com/spautz/react-hibernate/branches) [![test coverage](https://img.shields.io/coveralls/github/spautz/react-hibernate/master.svg)](https://coveralls.io/github/spautz/react-hibernate?branch=master) @@ -18,15 +16,15 @@ Instead of unmounting, the component tree goes into "hibernation". It will "wake internal state, as if you never left. - [React Router Hibernate](./packages/react-router-hibernate/) works per-route, if you're using - [React Router](https://reacttraining.com/react-router/) (v5) -- [React Hibernate](./packages/react-hibernate/) works for any React subtree + [React Router](https://reacttraining.com/react-router/) v5 +- [React Hibernate](./packages/react-hibernate/) works for any React subtree (not yet published) - [React Pauseable Containers](./packages/react-pauseable-containers/) improve performances by freezing all updates to hibernated subtrees [Cache options are available](https://github.com/spautz/limited-cache/#options) to control how many subtrees may be hibernated at a time, and for how long. -## Some Use Cases +## Use Cases - Screens which are slow to initialize: avoid initializing a second time - Form values: restore half-completed form fields if the user leaves and come back later @@ -34,8 +32,8 @@ hibernated at a time, and for how long. - Accordion panels, steps in a wizard, or other temporarily-hidden content In general, this is a "good enough" alternative to storing component state in Redux or an external cache: you _could_ -build a powerful system to track partially-completed forms, it would just take time and engineering. React Hibernate is -just a quick, easy way to get "good enough" coverage for the common cases. +build a powerful system to track internal state and restore partially-completed forms, it would just take development +time. React Hibernate is just a quick, easy way to get "good enough" coverage for the common cases. ## Packages diff --git a/packages/dev-helpers/README.md b/packages/dev-helpers/README.md index 505715a..f11e450 100644 --- a/packages/dev-helpers/README.md +++ b/packages/dev-helpers/README.md @@ -1,5 +1,5 @@ # React-Hibernate-Dev-Helpers -Utilities for demonstrating [React Hibernate](https://github.com/spautz/react-hibernate). +Internal utilities for demonstrating [React Hibernate](https://github.com/spautz/react-hibernate). ![private](https://img.shields.io/badge/npm-private-red.svg) diff --git a/packages/react-hibernate/README.md b/packages/react-hibernate/README.md index 3746839..9258277 100644 --- a/packages/react-hibernate/README.md +++ b/packages/react-hibernate/README.md @@ -22,8 +22,8 @@ This package provides the main functionality for [React Hibernate](https://githu - [x] Demos - [x] Monorepo - [ ] Initial release -- [ ] Explore: `useHibernatingEffect` hook (successfully prototyped) -- [ ] Explore: `maxCacheTime` override per-route (successfully prototyped) +- [ ] Add: `useHibernatingEffect` hook (successfully prototyped) +- [ ] Add: `maxCacheTime` override per-route (successfully prototyped) - [ ] Explore: Options to better control which/when to add a subtree - [ ] Explore: React-Router v6 diff --git a/packages/react-pauseable-containers/README.md b/packages/react-pauseable-containers/README.md index 28b625b..84d49cc 100644 --- a/packages/react-pauseable-containers/README.md +++ b/packages/react-pauseable-containers/README.md @@ -20,14 +20,19 @@ Each receives a `shouldUpdate` prop: when set to false, changes in outside value ## Components -#### PauseableComponentContainer +#### `` Prevents children from rerendering when the parent component updates. This is essentially just a typescript-friendly version of [react-static-container](https://github.com/reactjs/react-static-container/) -#### PauseableReduxContainer +#### `` + +Freezes any React context, to prevent subscribed components in the subtree from updating when it changes. + +#### `` Prevents subscribed components from rerendering when the redux state changes, or the return value from `useSelector`. +The `dispatchWhenPaused` prop controls the `canDispatch` option of a [Redux-Pauseable-Store](../redux-pauseable-store). ## How to use this @@ -35,5 +40,10 @@ With [React-Hibernate](../react-hibernate) or [React-Router-Hibernate](../react- components -- or a new component composed from several of them together -- can prevent subtrees from updating while they're hibernating, to avoid needless work. -These can also be used on their own to prevent updates in components that are still mounted, such as the background -screen behind a modal or dialog, or inactive tabs or panes when using a wizard, accordion, or tabs widget. +These can also be used on their own. + +#### Use Cases + +- The background screen behind a modal or dialog +- Inactive tabs or panes when using a wizard, accordion, or tabs widget. +- Freezing UI updates and interactions while performing reauthentication or other blocking tasks diff --git a/packages/react-router-hibernate/README.md b/packages/react-router-hibernate/README.md index e492dbc..755b8a2 100644 --- a/packages/react-router-hibernate/README.md +++ b/packages/react-router-hibernate/README.md @@ -1,8 +1,6 @@ # React-Router-Hibernate -**This package is in active development. Things will change rapidly, and it is not yet production-ready. Feedback is welcome.** - -A react-router Switch which can leave inactive routes mounted-but-inactive until you navigate back. +A React Router `` which can leave inactive routes mounted-but-inactive until you navigate back. Part of [React Hibernate](https://github.com/spautz/react-hibernate) @@ -12,7 +10,7 @@ Part of [React Hibernate](https://github.com/spautz/react-hibernate) ## Overview By defaut, navigating from one `` to another in react-router will unmount the old route's tree. -If you return to it, it will be recreated from scratch. +If you return to it, it will be remounted from scratch. This library adds `` and ``: drop-in replacements for `` and `` which do not immediately unmount components when you navigate away. If you return, the prior tree will be restored, @@ -34,7 +32,7 @@ import { HibernatingSwitch, HibernatingRoute } from 'react-router-hibernate'; - {/* If you have your own custom components, you can add an isHibernatingRoute prop */} + {/* If you have your own custom components, add an isHibernatingRoute prop */} @@ -53,8 +51,8 @@ Time after which a subtree is removed from the cache. Set a falsy value to disab #### `WrapperComponent` (React component, default: none) -A component which wraps all potentially-hibernatable routes. It receives a `shouldUpdate` prop. See the -"Preventing Extra Work" section below. +A component which wraps all potentially-hibernatable routes. It receives a `shouldUpdate` prop. +[`React-Pauseable-Containers`](../react-pauseable-containers) was created for this. ## How it Works @@ -76,35 +74,30 @@ For example: react-redux's [useSelector hook](https://react-redux.js.org/next/ap [forcing a rerender](https://github.com/reduxjs/react-redux/blob/5402f24db139f7ff01c7f873d136ea7ee3b8d1cb/src/hooks/useSelector.js#L15) outside of the normal render cycle by changing local state -- so suppressing the normal render cycle is not enough. -In most cases this is fine -- inactive subtrees still use minimal resources -- but if the component itself performs -a lot of work and has an expensive render then you may want to avoid running it at all. - -You can do this by replacing the contexts for inactive subtrees via `WrapperComponent`. For example, you can -suppress all redux-related updates by providing a new store whose contents are frozen at whatever moment the subtree -became inactive. This library includes a `PauseableReduxContainer` which does exactly that. - -This will cause one extra render cycle when a component becomes inactive (since the context values it receives will not -strictly equal what it received before) but with memoization you can skip most of the work. +In most cases this is fine -- inactive subtrees still use minimal resources -- but if the component render is slow +or has side effects then you may want to avoid running it at all. -Read more: [@TODO: move most of this section to docs] +You can do that by setting a `WrapperComponent` which halts context updates. +[`React-Pauseable-Containers`](../react-pauseable-containers) was created for this: you can use its components directly, +compose them together to make your `WrapperComponent`, or follow the examples to make your own from scratch. ## Roadmap - [x] Proof of concept - [x] Project scaffolding - [x] Core functionality -- [ ] Tests (in progress) +- [x] Tests - [x] Demos - [x] Monorepo -- [ ] Initial release -- [ ] Explore: `useHibernatingEffect` hook (successfully prototyped) -- [ ] Explore: `maxCacheTime` override per-route (successfully prototyped) +- [x] Initial release +- [ ] Add `useHibernatingEffect` hook (successfully prototyped) +- [ ] Add `maxCacheTime` override per-route (successfully prototyped) - [ ] Explore: Options to better control which/when to add a subtree - [ ] Explore: React-Router v6 #### Known Issues -"Cannot update a component from inside the function body of a different component" warning in React 16.13 +"Cannot update a component from inside the function body of a different component" warning in React 16.13+ - This will be addressed as part of supporting React-Router v6, when subtree activation will need to be done via a component instead of a callback. diff --git a/packages/redux-pauseable-store/README.md b/packages/redux-pauseable-store/README.md index 1e96a98..20fdbb6 100644 --- a/packages/redux-pauseable-store/README.md +++ b/packages/redux-pauseable-store/README.md @@ -1,7 +1,5 @@ # Redux-Pauseable-Store -**This package is in active development. Things will change rapidly, and it is not yet production-ready. Feedback is welcome.** - Derive one redux store from another, then pause it. Part of [React Hibernate](https://github.com/spautz/react-hibernate) @@ -14,28 +12,50 @@ Part of [React Hibernate](https://github.com/spautz/react-hibernate) You probably don't need this library. When used incorrectly it will do more harm than good. Consider using [``](../react-pauseable-containers#pauseablereduxcontainer) -from [React Pauseable Containers](../react-pauseable-containers) instead. +from [React-Pauseable-Containers](../react-pauseable-containers) instead. -## Overview +## Usage -Whenever Redux updates, its new data will be provided to all components that are subscribed to it. This library -interrupts that. +``` +import { createPauseableStore } from 'redux-pauseable-store'; -## Details +const pauseableStore = createPauseableStore(store, options); +``` -If you want to **completely** stop a subtree from rerendering for some time -- as [React Router Hibernate](../react-router-hibernate/), -[React hibernate](../react-hibernate), and [React Pauseable Containers](../react-pauseable-containers) do -- then you -have to prevent anything which would trigger a state change in any component in that subtree. +`pauseableStore` is _derived from_ the original store, or parent store. It can be paused and unpaused at any time. +When unpaused, any state updates in the parent store will flow directly into the derived store. -React-Redux's [useSelector hook](https://react-redux.js.org/next/api/hooks#useselector) works by -[forcing a rerender](https://github.com/reduxjs/react-redux/blob/5402f24db139f7ff01c7f873d136ea7ee3b8d1cb/src/hooks/useSelector.js#L15) -outside of the normal render cycle: it triggers a state change. +You can subscribe to and dispatch actions to the derived store, just like the original store. -This library overwrites the context provider for React Redux to trick it into subscribing to a second Redux store -- -which we can then pause and unpause. +## Options -## Usage +Options can be set at initialization, through helper functions, or by updating the value directly on the store instance. + +#### isPaused (boolean, default: `false`) + +Whether the pauseable store receives state updates from its parent store. + +``` +pauseableStore.isPaused; // boolean +pauseableStore.setPaused(boolean); +``` + +#### canDispatch (tri-state boolean, default: `'warn'`) + +Whether the pauseable store can dispatch actions to its parent store. When enabled, any actions dispatched to the +derived store will instead be routed to the parent store. When to set `'warn'`, a warning will be emitted +(but the action will still be dispatched.) + +``` +pauseableStore.canDispatch; // boolean | 'warn' +pauseableStore.setDispatch(boolean | 'warn'); +``` + +#### notifyListersOnUnpause (boolean, default: `true`) + +Whether to notify subscribed listeners when the store unpauses, if it was updated while paused. You probably want to +keep this enabled. ``` -// @TODO +pauseableStore.notifyListersOnUnpause; // boolean ```