Skip to content

Commit

Permalink
[#796] Add help for Strict Mode in README
Browse files Browse the repository at this point in the history
Fixes #796
  • Loading branch information
stefcameron committed Sep 10, 2022
1 parent cb8d098 commit 459ecf4
Showing 1 changed file with 31 additions and 6 deletions.
37 changes: 31 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,9 +145,34 @@ ReactDOM.render(<Demo />, document.getElementById('root')); // React 16-17
createRoot(document.getElementById('root')).render(<Demo />); // React 18
```

### Props
## ❗️❗️ React 18 Strict Mode ❗️❗️

#### children
React 18 introduced [new behavior](https://reactjs.org/docs/strict-mode.html#ensuring-reusable-state) in Strict Mode whereby it mimics a possible future behavior where React might optimize an app's performance by arbitrarily unmounting components that aren't in use and later remount them (with previous, reused state -- that's the key) when the user needs them again. What constitutes "not in use" and "needs them again" is as yet undefined.

It should be noted this violates stated behavior about [componentWillUnmount](https://reactjs.org/docs/react-component.html#componentwillunmount): "Once a component instance is unmounted, __it will never be mounted again.__" Not so in Strict Mode!

Nonetheless, many of you like to use Strict Mode and may not realize what changed in React 18, so we have made our __[best attempt](https://github.com/focus-trap/focus-trap-react/pull/721) at supporting it in v9.0.2__ whereby the trap attempts to detect that it has been remounted with previous state: If the `active` prop's value is `true`, and an internal focus trap instance already exists, the focus trap is re-activated on remount in order to reconcile stated expectations.

> 🚨 In Strict Mode (and so in dev builds only, since this behavior of Strict Mode only affects dev builds), the trap __will be deactivated as soon as it is mounted__, and then reactivated again, almost immediately, because React will immediately unmount and remount the trap as soon as it's rendered.
Therefore, __do not use options like onActivate, onPostActivate, onDeactivate, or onPostDeactivate to affect component state__.

<details>
<summary>Explanation and sample anti-pattern to <strong>avoid</strong></summary>
<p>
See <a href="https://github.com/focus-trap/focus-trap-react/issues/796">this discussion</a> for an example sandbox (issue description) where <code>onDeactivate</code> was used to trigger the close of a dialog when the trap was deactivated (e.g. to react to the user clicking outside the trap with <code>focusTrapOptions.clickOutsideDeactivates=true</code>).
</p>
<p>
The result can be that (depending on how you render the trap) in Strict Mode, the dialog never appears because it gets closed as soon as the trap renders, since the trap is deactivated as soon as it's unmounted, and so the <code>onDeactivate</code> handler is called, thus hiding the dialog...
</p>
<p>
<strong>This is intentional</strong>: If the trap gets unmounted, it has no idea if it's being unmounted <em>for good</em> or if it's going to be remounted <em>at some future point in time</em>. It also has no idea of knowing <em>how long</em> it will be until it's remounted again. So it must be deactivated as though it's going away for good in order to prevent unintentional behavior and memory leaks (from orphaned document event listeners).
</p>
</details>

## Props

### children

> ⚠️ The `<FocusTrap>` component requires a __single__ child, and this child must __forward refs__ onto the element which will ultimately be considered the trap's container. Since React does not provide for a way to forward refs to class-based components, this means the child must be a __functional__ component that uses the `React.forwardRef()` API.
>
Expand Down Expand Up @@ -237,29 +262,29 @@ const root = createRoot(container);
root.render(<DemoFunctionChild />);
```

#### focusTrapOptions
### focusTrapOptions

Type: `Object`, optional

Pass any of the options available in focus-trap's [createOptions](https://github.com/focus-trap/focus-trap#createoptions).

> ⚠️ See notes about __[testing in JSDom](#testing-in-jsdom)__ (e.g. using Jest) if that's what you currently use.
#### active
### active

Type: `Boolean`, optional

By default, the `FocusTrap` activates when it mounts. So you activate and deactivate it via mounting and unmounting. If, however, you want to keep the `FocusTrap` mounted *while still toggling its activation state*, you can do that with this prop.

See `demo/demo-special-element.js`.

#### paused
### paused

Type: `Boolean`, optional

If you would like to pause or unpause the focus trap (see [`focus-trap`'s documentation](https://github.com/focus-trap/focus-trap#focustrappause)), toggle this prop.

#### containerElements
### containerElements

Type: `Array of HTMLElement`, optional

Expand Down

0 comments on commit 459ecf4

Please sign in to comment.