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 e6ed09f
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, depending on how you render the <code><FocusTrap></code>, can be that, in Strict Mode, the dialog never appears because it's closed as soon as the trap renders, since the trap is deactivated as soon as it's unmounted.
</p>
<p>
<strong>This is intentional</strong>, because if the component gets unmounted, the library has no idea if it's being unmounted <em>for good</em> or if it's going to be remounted <strong>at some future point in time</strong>. It also has no idea of knowing <em>how long</em> it will be until it's remounted again. So it must be deactivated 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 e6ed09f

Please sign in to comment.