Skip to content

Commit

Permalink
Update docs and READMEs
Browse files Browse the repository at this point in the history
  • Loading branch information
spautz committed Oct 5, 2020
1 parent da5c1fd commit f1f1601
Show file tree
Hide file tree
Showing 6 changed files with 74 additions and 53 deletions.
12 changes: 5 additions & 7 deletions README.md
Expand Up @@ -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)

Expand All @@ -18,24 +16,24 @@ 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
- Background tasks like file uploads: dispatch actions from unmounted components
- 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

Expand Down
2 changes: 1 addition & 1 deletion 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)
4 changes: 2 additions & 2 deletions packages/react-hibernate/README.md
Expand Up @@ -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

Expand Down
18 changes: 14 additions & 4 deletions packages/react-pauseable-containers/README.md
Expand Up @@ -20,20 +20,30 @@ Each receives a `shouldUpdate` prop: when set to false, changes in outside value

## Components

#### PauseableComponentContainer
#### `<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
#### `<PauseableContextContainer Context={...}>`

Freezes any React context, to prevent subscribed components in the subtree from updating when it changes.

#### `<PauseableReduxContainer dispatchWhenPaused={boolean}>`

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

With [React-Hibernate](../react-hibernate) or [React-Router-Hibernate](../react-router-hibernate/), using one of these
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
37 changes: 15 additions & 22 deletions 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 `<Switch>` which can leave inactive routes mounted-but-inactive until you navigate back.

Part of [React Hibernate](https://github.com/spautz/react-hibernate)

Expand All @@ -12,7 +10,7 @@ Part of [React Hibernate](https://github.com/spautz/react-hibernate)
## Overview

By defaut, navigating from one `<Route>` 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 `<HibernatingSwitch>` and `<HibernatingRoute>`: drop-in replacements for `<Switch>` and `<Route>`
which do not immediately unmount components when you navigate away. If you return, the prior tree will be restored,
Expand All @@ -34,7 +32,7 @@ import { HibernatingSwitch, HibernatingRoute } from 'react-router-hibernate';
<Route path="/baz" component={...} />
<Redirect from="/something-else" to="/foo" />

{/* If you have your own custom components, you can add an isHibernatingRoute prop */}
{/* If you have your own custom components, add an isHibernatingRoute prop */}
<MyPrivateRoute path="/secret" component={...} isHibernatingRoute />

</HibernatingSwitch>
Expand All @@ -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

Expand All @@ -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.
54 changes: 37 additions & 17 deletions 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)
Expand All @@ -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 [`<PauseableReduxContainer>`](../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
```

0 comments on commit f1f1601

Please sign in to comment.