How to patch a package with yarn

How to patch a package with yarn

Sometimes you need to quickly fix an npm package without waiting for a new version to be published which usually takes time.

In this post, I will show you how to do that with yarn 2, without using any additional tools.
For yarn 1 users, please use the patch-package package to do that.


Yarn 2 has various package protocols (link) that support various purposes: npm, git, github, file, link, patch, workspace, etc. To patch a package, we will use the patch protocol.

In this post, I will use the react-transition-group (version 4.4.5) as an example. This package has a long-waiting bug in react 18 (issue link). The fixing commit is provided (commit link), but the author has not merged or published a fixed version. Through this tutorial, we will learn how to patch the package in your project.

Step 0: make sure you are using yarn 2

yarn --version
version 1.x is v1, version 2.x and upper is called v2.

If not, switch to v2 from legacy v1.

yarn set version stable

Step 1: Install the package

yarn add react-transition-group

Step 2: Prepare the patch

yarn patch react-transition-group

Result

➤ YN0000: Package react-transition-group@npm:4.4.5 got extracted with success!
➤ YN0000: You can now edit the following folder: /tmp/xfs-f933a619/user
➤ YN0000: Once you are done run yarn patch-commit -s /tmp/xfs-f933a619/user and Yarn will store a patchfile based on your changes.
➤ YN0000: Done in 0s 51ms

The command will output a temporary directory (e.,g., /tmp/xfs-f933a619/user), in which you should apply your modification to the package.

Step 3: Prepare the fixed version

This step varies by package to package.

  • For the current package, download the page from GitHub.
  • Replace the content of src/Transition.js with the content in the fixed commit here.
  • Install the required packages for compilation with yarn.
  • Compile the fixed version with yarn build. For this package, the output is placed in the lib directory. This behavior is different from package to package. We need to figure it out ourselves.
  • Copy the content of lib to the directory outputted in step 2.
 cp -R lib/* /tmp/xfs-f933a619/user/

Step 4: generate and store the patch file

Run the command generated in step 2

yarn patch-commit -s /tmp/xfs-f933a619/user

package.json will change from

  "dependencies": {
    "react-transition-group": "^4.4.5"
  }

to

  "resolutions": {
    "react-transition-group@^4.4.5": "patch:react-transition-group@npm%3A4.4.5#./.yarn/patches/react-transition-group-npm-4.4.5-98ea4ef96e.patch"
  },
  "dependencies": {
    "react-transition-group": "^4.4.5"
  }

With the patch file generated at .yarn/patches/react-transition-group-npm-4.4.5-98ea4ef96e.patch. Remember to add this file to git, otherwise, other users cannot install the package.

diff --git a/cjs/CSSTransition.js b/cjs/CSSTransition.js
index 8f0d3c1411e76293894b46ee6b9817609d2f192e..2a5932ff13877078bd134b7b7a166a1f5e689c37 100644
--- a/cjs/CSSTransition.js
+++ b/cjs/CSSTransition.js
@@ -52,10 +52,11 @@ var removeClass = function removeClass(node, classes) {
  * ```jsx
  * function App() {
  *   const [inProp, setInProp] = useState(false);
+ *   const nodeRef = useRef(null);
  *   return (
  *     <div>
- *       <CSSTransition in={inProp} timeout={200} classNames="my-node">
- *         <div>
+ *       <CSSTransition nodeRef={nodeRef} in={inProp} timeout={200} classNames="my-node">
+ *         <div ref={nodeRef}>
  *           {"I'll receive my-node-* classes"}
  *         </div>
  *       </CSSTransition>
@@ -370,7 +371,7 @@ CSSTransition.propTypes = process.env.NODE_ENV !== "production" ? _extends({}, _
    * A `<Transition>` callback fired immediately after the 'enter' or 'appear' class is
    * applied.
    *
-   * **Note**: when `nodeRef` prop is passed, `node` is not passed.
+   * **Note**: when `nodeRef` prop is passed, `node` is not passed, so `isAppearing` is being passed as the first argument.
    *
    * @type Function(node: HtmlElement, isAppearing: bool)
    */
@@ -380,7 +381,7 @@ CSSTransition.propTypes = process.env.NODE_ENV !== "production" ? _extends({}, _
    * A `<Transition>` callback fired immediately after the 'enter-active' or
    * 'appear-active' class is applied.
    *
-   * **Note**: when `nodeRef` prop is passed, `node` is not passed.
+   * **Note**: when `nodeRef` prop is passed, `node` is not passed, so `isAppearing` is being passed as the first argument.
    *
    * @type Function(node: HtmlElement, isAppearing: bool)
    */
@@ -390,7 +391,7 @@ CSSTransition.propTypes = process.env.NODE_ENV !== "production" ? _extends({}, _
    * A `<Transition>` callback fired immediately after the 'enter' or
    * 'appear' classes are **removed** and the `done` class is added to the DOM node.
    *
-   * **Note**: when `nodeRef` prop is passed, `node` is not passed.
+   * **Note**: when `nodeRef` prop is passed, `node` is not passed, so `isAppearing` is being passed as the first argument.
    *
    * @type Function(node: HtmlElement, isAppearing: bool)
    */
diff --git a/cjs/SwitchTransition.js b/cjs/SwitchTransition.js
index 19b342c30fd281ec494ef5d2357278daffebac7a..eea69ae69a3d13a21a297f63bb96b360d25813e9 100644
--- a/cjs/SwitchTransition.js
+++ b/cjs/SwitchTransition.js
@@ -110,14 +110,18 @@ var enterRenders = (_enterRenders = {}, _enterRenders[modes.out] = function (_re
  * ```jsx
  * function App() {
  *  const [state, setState] = useState(false);
+ *  const helloRef = useRef(null);
+ *  const goodbyeRef = useRef(null);
+ *  const nodeRef = state ? goodbyeRef : helloRef;
  *  return (
  *    <SwitchTransition>
  *      <CSSTransition
  *        key={state ? "Goodbye, world!" : "Hello, world!"}
+ *        nodeRef={nodeRef}
  *        addEndListener={(node, done) => node.addEventListener("transitionend", done, false)}
  *        classNames='fade'
  *      >
- *        <button onClick={() => setState(state => !state)}>
+ *        <button ref={nodeRef} onClick={() => setState(state => !state)}>
  *          {state ? "Goodbye, world!" : "Hello, world!"}
  *        </button>
  *      </CSSTransition>
diff --git a/cjs/Transition.js b/cjs/Transition.js
index b44cb76052fba3d1cd1d8d034e5c79b6d7493a19..8c5e815d83b5e59cb90eee11aa571c02b7d53890 100644
--- a/cjs/Transition.js
+++ b/cjs/Transition.js
@@ -7,7 +7,7 @@ var _propTypes = _interopRequireDefault(require("prop-types"));
 
 var _react = _interopRequireDefault(require("react"));
 
-var _reactDom = _interopRequireDefault(require("react-dom"));
+var _reactDom = _interopRequireWildcard(require("react-dom"));
 
 var _config = _interopRequireDefault(require("./config"));
 
@@ -17,6 +17,10 @@ var _TransitionGroupContext = _interopRequireDefault(require("./TransitionGroupC
 
 var _reflow = require("./utils/reflow");
 
+function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function _getRequireWildcardCache() { return cache; }; return cache; }
+
+function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
+
 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
 
 function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
@@ -56,6 +60,7 @@ var EXITING = 'exiting';
  *
  * ```jsx
  * import { Transition } from 'react-transition-group';
+ * import { useRef } from 'react';
  *
  * const duration = 300;
  *
@@ -71,18 +76,21 @@ var EXITING = 'exiting';
  *   exited:  { opacity: 0 },
  * };
  *
- * const Fade = ({ in: inProp }) => (
- *   <Transition in={inProp} timeout={duration}>
- *     {state => (
- *       <div style={{
- *         ...defaultStyle,
- *         ...transitionStyles[state]
- *       }}>
- *         I'm a fade Transition!
- *       </div>
- *     )}
- *   </Transition>
- * );
+ * function Fade({ in: inProp }) {
+ *   const nodeRef = useRef(null);
+ *   return (
+ *     <Transition nodeRef={nodeRef} in={inProp} timeout={duration}>
+ *       {state => (
+ *         <div ref={nodeRef} style={{
+ *           ...defaultStyle,
+ *           ...transitionStyles[state]
+ *         }}>
+ *           I'm a fade Transition!
+ *         </div>
+ *       )}
+ *     </Transition>
+ *   );
+ * }
  * ```
  *
  * There are 4 main states a Transition can be in:
@@ -99,11 +107,15 @@ var EXITING = 'exiting';
  * [useState](https://reactjs.org/docs/hooks-reference.html#usestate) hook):
  *
  * ```jsx
+ * import { Transition } from 'react-transition-group';
+ * import { useState, useRef } from 'react';
+ *
  * function App() {
  *   const [inProp, setInProp] = useState(false);
+ *   const nodeRef = useRef(null);
  *   return (
  *     <div>
- *       <Transition in={inProp} timeout={500}>
+ *       <Transition nodeRef={nodeRef} in={inProp} timeout={500}>
  *         {state => (
  *           // ...
  *         )}
@@ -343,25 +355,48 @@ var Transition = /*#__PURE__*/function (_React$Component) {
       this.nextCallback.cancel();
       this.nextCallback = null;
     }
-  };
+  } // safeSetState(nextState, callback) {
+  //   // This shouldn't be necessary, but there are weird race conditions with
+  //   // setState callbacks and unmounting in testing, so always make sure that
+  //   // we can cancel any pending setState callbacks after we unmount.
+  //   callback = this.setNextCallback(callback);
+  //   this.setState(nextState, callback);
+  // }
+
+  /**
+   * Prevent React 18 from batching updates.
+   * https://github.com/reactjs/react-transition-group/issues/816#issuecomment-1175783666
+   *
+   * @param {object} nextState Next state object.
+   * @param {function} callback Post-set callback.
+   */
+  ;
 
   _proto.safeSetState = function safeSetState(nextState, callback) {
-    // This shouldn't be necessary, but there are weird race conditions with
-    // setState callbacks and unmounting in testing, so always make sure that
-    // we can cancel any pending setState callbacks after we unmount.
-    callback = this.setNextCallback(callback);
-    this.setState(nextState, callback);
+    var _this4 = this;
+
+    // console.log(`Transition.js setStateConditionalFlushSync nextState=${JSON.stringify(nextState)}`)
+    if ((nextState == null ? void 0 : nextState.status) !== "exited") {
+      // console.log(`Transition.js setStateConditionalFlushSync *imperative* nextState=${JSON.stringify(nextState)}`)
+      this.setState(nextState, callback);
+      return;
+    }
+
+    (0, _reactDom.flushSync)(function () {
+      // console.log(`Transition.js setStateConditionalFlushSync *flushSync* nextState=${JSON.stringify(nextState)}`)
+      _this4.setState(nextState, callback);
+    });
   };
 
   _proto.setNextCallback = function setNextCallback(callback) {
-    var _this4 = this;
+    var _this5 = this;
 
     var active = true;
 
     this.nextCallback = function (event) {
       if (active) {
         active = false;
-        _this4.nextCallback = null;
+        _this5.nextCallback = null;
         callback(event);
       }
     };
@@ -437,9 +472,12 @@ var Transition = /*#__PURE__*/function (_React$Component) {
 Transition.contextType = _TransitionGroupContext.default;
 Transition.propTypes = process.env.NODE_ENV !== "production" ? {
   /**
-   * A React reference to DOM element that need to transition:
+   * A React reference to the DOM element that needs to transition:
    * https://stackoverflow.com/a/51127130/4671932
    *
+   *   - This prop is optional, but recommended in order to avoid defaulting to
+   *      [`ReactDOM.findDOMNode`](https://reactjs.org/docs/react-dom.html#finddomnode),
+   *      which is deprecated in `StrictMode`
    *   - When `nodeRef` prop is used, `node` is not passed to callback functions
    *      (e.g. `onEnter`) because user already has direct access to the node.
    *   - When changing `key` prop of `Transition` in a `TransitionGroup` a new
@@ -461,9 +499,9 @@ Transition.propTypes = process.env.NODE_ENV !== "production" ? {
    * specific props to a component.
    *
    * ```jsx
-   * <Transition in={this.state.in} timeout={150}>
+   * <Transition nodeRef={nodeRef} in={this.state.in} timeout={150}>
    *   {state => (
-   *     <MyComponent className={`fade fade-${state}`} />
+   *     <MyComponent ref={nodeRef} className={`fade fade-${state}`} />
    *   )}
    * </Transition>
    * ```
@@ -554,7 +592,7 @@ Transition.propTypes = process.env.NODE_ENV !== "production" ? {
    * DOM node and a `done` callback. Allows for more fine grained transition end
    * logic. Timeouts are still used as a fallback if provided.
    *
-   * **Note**: when `nodeRef` prop is passed, `node` is not passed.
+   * **Note**: when `nodeRef` prop is passed, `node` is not passed, so `done` is being passed as the first argument.
    *
    * ```jsx
    * addEndListener={(node, done) => {
@@ -569,7 +607,7 @@ Transition.propTypes = process.env.NODE_ENV !== "production" ? {
    * Callback fired before the "entering" status is applied. An extra parameter
    * `isAppearing` is supplied to indicate if the enter stage is occurring on the initial mount
    *
-   * **Note**: when `nodeRef` prop is passed, `node` is not passed.
+   * **Note**: when `nodeRef` prop is passed, `node` is not passed, so `isAppearing` is being passed as the first argument.
    *
    * @type Function(node: HtmlElement, isAppearing: bool) -> void
    */
@@ -579,7 +617,7 @@ Transition.propTypes = process.env.NODE_ENV !== "production" ? {
    * Callback fired after the "entering" status is applied. An extra parameter
    * `isAppearing` is supplied to indicate if the enter stage is occurring on the initial mount
    *
-   * **Note**: when `nodeRef` prop is passed, `node` is not passed.
+   * **Note**: when `nodeRef` prop is passed, `node` is not passed, so `isAppearing` is being passed as the first argument.
    *
    * @type Function(node: HtmlElement, isAppearing: bool)
    */
@@ -589,7 +627,7 @@ Transition.propTypes = process.env.NODE_ENV !== "production" ? {
    * Callback fired after the "entered" status is applied. An extra parameter
    * `isAppearing` is supplied to indicate if the enter stage is occurring on the initial mount
    *
-   * **Note**: when `nodeRef` prop is passed, `node` is not passed.
+   * **Note**: when `nodeRef` prop is passed, `node` is not passed, so `isAppearing` is being passed as the first argument.
    *
    * @type Function(node: HtmlElement, isAppearing: bool) -> void
    */
diff --git a/dist/react-transition-group.js b/dist/react-transition-group.js
index a16945276d5582f0d9e2feb071808b639ede8f3f..5d7acfe23fadbb472906f013f59dd22608affcb3 100644
--- a/dist/react-transition-group.js
+++ b/dist/react-transition-group.js
@@ -5,7 +5,7 @@
 }(this, (function (exports, React, ReactDOM) { 'use strict';
 
   var React__default = 'default' in React ? React['default'] : React;
-  ReactDOM = ReactDOM && Object.prototype.hasOwnProperty.call(ReactDOM, 'default') ? ReactDOM['default'] : ReactDOM;
+  var ReactDOM__default = 'default' in ReactDOM ? ReactDOM['default'] : ReactDOM;
 
   function _extends() {
     _extends = Object.assign || function (target) {
@@ -1132,6 +1132,7 @@
    *
    * ```jsx
    * import { Transition } from 'react-transition-group';
+   * import { useRef } from 'react';
    *
    * const duration = 300;
    *
@@ -1147,18 +1148,21 @@
    *   exited:  { opacity: 0 },
    * };
    *
-   * const Fade = ({ in: inProp }) => (
-   *   <Transition in={inProp} timeout={duration}>
-   *     {state => (
-   *       <div style={{
-   *         ...defaultStyle,
-   *         ...transitionStyles[state]
-   *       }}>
-   *         I'm a fade Transition!
-   *       </div>
-   *     )}
-   *   </Transition>
-   * );
+   * function Fade({ in: inProp }) {
+   *   const nodeRef = useRef(null);
+   *   return (
+   *     <Transition nodeRef={nodeRef} in={inProp} timeout={duration}>
+   *       {state => (
+   *         <div ref={nodeRef} style={{
+   *           ...defaultStyle,
+   *           ...transitionStyles[state]
+   *         }}>
+   *           I'm a fade Transition!
+   *         </div>
+   *       )}
+   *     </Transition>
+   *   );
+   * }
    * ```
    *
    * There are 4 main states a Transition can be in:
@@ -1175,11 +1179,15 @@
    * [useState](https://reactjs.org/docs/hooks-reference.html#usestate) hook):
    *
    * ```jsx
+   * import { Transition } from 'react-transition-group';
+   * import { useState, useRef } from 'react';
+   *
    * function App() {
    *   const [inProp, setInProp] = useState(false);
+   *   const nodeRef = useRef(null);
    *   return (
    *     <div>
-   *       <Transition in={inProp} timeout={500}>
+   *       <Transition nodeRef={nodeRef} in={inProp} timeout={500}>
    *         {state => (
    *           // ...
    *         )}
@@ -1323,7 +1331,7 @@
 
         if (nextStatus === ENTERING) {
           if (this.props.unmountOnExit || this.props.mountOnEnter) {
-            var node = this.props.nodeRef ? this.props.nodeRef.current : ReactDOM.findDOMNode(this); // https://github.com/reactjs/react-transition-group/pull/749
+            var node = this.props.nodeRef ? this.props.nodeRef.current : ReactDOM__default.findDOMNode(this); // https://github.com/reactjs/react-transition-group/pull/749
             // With unmountOnExit or mountOnEnter, the enter animation should happen at the transition between `exited` and `entering`.
             // To make the animation happen,  we have to separate each rendering and avoid being processed as batched.
 
@@ -1347,7 +1355,7 @@
       var enter = this.props.enter;
       var appearing = this.context ? this.context.isMounting : mounting;
 
-      var _ref2 = this.props.nodeRef ? [appearing] : [ReactDOM.findDOMNode(this), appearing],
+      var _ref2 = this.props.nodeRef ? [appearing] : [ReactDOM__default.findDOMNode(this), appearing],
           maybeNode = _ref2[0],
           maybeAppearing = _ref2[1];
 
@@ -1385,7 +1393,7 @@
 
       var exit = this.props.exit;
       var timeouts = this.getTimeouts();
-      var maybeNode = this.props.nodeRef ? undefined : ReactDOM.findDOMNode(this); // no exit animation skip right to EXITED
+      var maybeNode = this.props.nodeRef ? undefined : ReactDOM__default.findDOMNode(this); // no exit animation skip right to EXITED
 
       if (!exit || config.disabled) {
         this.safeSetState({
@@ -1417,25 +1425,48 @@
         this.nextCallback.cancel();
         this.nextCallback = null;
       }
-    };
+    } // safeSetState(nextState, callback) {
+    //   // This shouldn't be necessary, but there are weird race conditions with
+    //   // setState callbacks and unmounting in testing, so always make sure that
+    //   // we can cancel any pending setState callbacks after we unmount.
+    //   callback = this.setNextCallback(callback);
+    //   this.setState(nextState, callback);
+    // }
+
+    /**
+     * Prevent React 18 from batching updates.
+     * https://github.com/reactjs/react-transition-group/issues/816#issuecomment-1175783666
+     *
+     * @param {object} nextState Next state object.
+     * @param {function} callback Post-set callback.
+     */
+    ;
 
     _proto.safeSetState = function safeSetState(nextState, callback) {
-      // This shouldn't be necessary, but there are weird race conditions with
-      // setState callbacks and unmounting in testing, so always make sure that
-      // we can cancel any pending setState callbacks after we unmount.
-      callback = this.setNextCallback(callback);
-      this.setState(nextState, callback);
+      var _this4 = this;
+
+      // console.log(`Transition.js setStateConditionalFlushSync nextState=${JSON.stringify(nextState)}`)
+      if ((nextState == null ? void 0 : nextState.status) !== "exited") {
+        // console.log(`Transition.js setStateConditionalFlushSync *imperative* nextState=${JSON.stringify(nextState)}`)
+        this.setState(nextState, callback);
+        return;
+      }
+
+      ReactDOM.flushSync(function () {
+        // console.log(`Transition.js setStateConditionalFlushSync *flushSync* nextState=${JSON.stringify(nextState)}`)
+        _this4.setState(nextState, callback);
+      });
     };
 
     _proto.setNextCallback = function setNextCallback(callback) {
-      var _this4 = this;
+      var _this5 = this;
 
       var active = true;
 
       this.nextCallback = function (event) {
         if (active) {
           active = false;
-          _this4.nextCallback = null;
+          _this5.nextCallback = null;
           callback(event);
         }
       };
@@ -1449,7 +1480,7 @@
 
     _proto.onTransitionEnd = function onTransitionEnd(timeout, handler) {
       this.setNextCallback(handler);
-      var node = this.props.nodeRef ? this.props.nodeRef.current : ReactDOM.findDOMNode(this);
+      var node = this.props.nodeRef ? this.props.nodeRef.current : ReactDOM__default.findDOMNode(this);
       var doesNotHaveTimeoutOrListener = timeout == null && !this.props.addEndListener;
 
       if (!node || doesNotHaveTimeoutOrListener) {
@@ -1511,9 +1542,12 @@
   Transition.contextType = TransitionGroupContext;
   Transition.propTypes =  {
     /**
-     * A React reference to DOM element that need to transition:
+     * A React reference to the DOM element that needs to transition:
      * https://stackoverflow.com/a/51127130/4671932
      *
+     *   - This prop is optional, but recommended in order to avoid defaulting to
+     *      [`ReactDOM.findDOMNode`](https://reactjs.org/docs/react-dom.html#finddomnode),
+     *      which is deprecated in `StrictMode`
      *   - When `nodeRef` prop is used, `node` is not passed to callback functions
      *      (e.g. `onEnter`) because user already has direct access to the node.
      *   - When changing `key` prop of `Transition` in a `TransitionGroup` a new
@@ -1535,9 +1569,9 @@
      * specific props to a component.
      *
      * ```jsx
-     * <Transition in={this.state.in} timeout={150}>
+     * <Transition nodeRef={nodeRef} in={this.state.in} timeout={150}>
      *   {state => (
-     *     <MyComponent className={`fade fade-${state}`} />
+     *     <MyComponent ref={nodeRef} className={`fade fade-${state}`} />
      *   )}
      * </Transition>
      * ```
@@ -1628,7 +1662,7 @@
      * DOM node and a `done` callback. Allows for more fine grained transition end
      * logic. Timeouts are still used as a fallback if provided.
      *
-     * **Note**: when `nodeRef` prop is passed, `node` is not passed.
+     * **Note**: when `nodeRef` prop is passed, `node` is not passed, so `done` is being passed as the first argument.
      *
      * ```jsx
      * addEndListener={(node, done) => {
@@ -1643,7 +1677,7 @@
      * Callback fired before the "entering" status is applied. An extra parameter
      * `isAppearing` is supplied to indicate if the enter stage is occurring on the initial mount
      *
-     * **Note**: when `nodeRef` prop is passed, `node` is not passed.
+     * **Note**: when `nodeRef` prop is passed, `node` is not passed, so `isAppearing` is being passed as the first argument.
      *
      * @type Function(node: HtmlElement, isAppearing: bool) -> void
      */
@@ -1653,7 +1687,7 @@
      * Callback fired after the "entering" status is applied. An extra parameter
      * `isAppearing` is supplied to indicate if the enter stage is occurring on the initial mount
      *
-     * **Note**: when `nodeRef` prop is passed, `node` is not passed.
+     * **Note**: when `nodeRef` prop is passed, `node` is not passed, so `isAppearing` is being passed as the first argument.
      *
      * @type Function(node: HtmlElement, isAppearing: bool)
      */
@@ -1663,7 +1697,7 @@
      * Callback fired after the "entered" status is applied. An extra parameter
      * `isAppearing` is supplied to indicate if the enter stage is occurring on the initial mount
      *
-     * **Note**: when `nodeRef` prop is passed, `node` is not passed.
+     * **Note**: when `nodeRef` prop is passed, `node` is not passed, so `isAppearing` is being passed as the first argument.
      *
      * @type Function(node: HtmlElement, isAppearing: bool) -> void
      */
@@ -1746,10 +1780,11 @@
    * ```jsx
    * function App() {
    *   const [inProp, setInProp] = useState(false);
+   *   const nodeRef = useRef(null);
    *   return (
    *     <div>
-   *       <CSSTransition in={inProp} timeout={200} classNames="my-node">
-   *         <div>
+   *       <CSSTransition nodeRef={nodeRef} in={inProp} timeout={200} classNames="my-node">
+   *         <div ref={nodeRef}>
    *           {"I'll receive my-node-* classes"}
    *         </div>
    *       </CSSTransition>
@@ -2064,7 +2099,7 @@
      * A `<Transition>` callback fired immediately after the 'enter' or 'appear' class is
      * applied.
      *
-     * **Note**: when `nodeRef` prop is passed, `node` is not passed.
+     * **Note**: when `nodeRef` prop is passed, `node` is not passed, so `isAppearing` is being passed as the first argument.
      *
      * @type Function(node: HtmlElement, isAppearing: bool)
      */
@@ -2074,7 +2109,7 @@
      * A `<Transition>` callback fired immediately after the 'enter-active' or
      * 'appear-active' class is applied.
      *
-     * **Note**: when `nodeRef` prop is passed, `node` is not passed.
+     * **Note**: when `nodeRef` prop is passed, `node` is not passed, so `isAppearing` is being passed as the first argument.
      *
      * @type Function(node: HtmlElement, isAppearing: bool)
      */
@@ -2084,7 +2119,7 @@
      * A `<Transition>` callback fired immediately after the 'enter' or
      * 'appear' classes are **removed** and the `done` class is added to the DOM node.
      *
-     * **Note**: when `nodeRef` prop is passed, `node` is not passed.
+     * **Note**: when `nodeRef` prop is passed, `node` is not passed, so `isAppearing` is being passed as the first argument.
      *
      * @type Function(node: HtmlElement, isAppearing: bool)
      */
@@ -2533,7 +2568,7 @@
       if (child.props[handler]) (_child$props = child.props)[handler].apply(_child$props, originalArgs);
 
       if (this.props[handler]) {
-        var maybeNode = child.props.nodeRef ? undefined : ReactDOM.findDOMNode(this);
+        var maybeNode = child.props.nodeRef ? undefined : ReactDOM__default.findDOMNode(this);
         this.props[handler](maybeNode);
       }
     };
@@ -2672,14 +2707,18 @@
    * ```jsx
    * function App() {
    *  const [state, setState] = useState(false);
+   *  const helloRef = useRef(null);
+   *  const goodbyeRef = useRef(null);
+   *  const nodeRef = state ? goodbyeRef : helloRef;
    *  return (
    *    <SwitchTransition>
    *      <CSSTransition
    *        key={state ? "Goodbye, world!" : "Hello, world!"}
+   *        nodeRef={nodeRef}
    *        addEndListener={(node, done) => node.addEventListener("transitionend", done, false)}
    *        classNames='fade'
    *      >
-   *        <button onClick={() => setState(state => !state)}>
+   *        <button ref={nodeRef} onClick={() => setState(state => !state)}>
    *          {state ? "Goodbye, world!" : "Hello, world!"}
    *        </button>
    *      </CSSTransition>
diff --git a/dist/react-transition-group.min.js b/dist/react-transition-group.min.js
index 0599e96071cefa78a34a3765b8408dbcc97d356d..c71721514ad0243ce5c306c1bac5ba61dd84dd98 100644
--- a/dist/react-transition-group.min.js
+++ b/dist/react-transition-group.min.js
@@ -1,4 +1,4 @@
-!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("react"),require("react-dom")):"function"==typeof define&&define.amd?define(["exports","react","react-dom"],t):t((e=e||self).ReactTransitionGroup={},e.React,e.ReactDOM)}(this,(function(e,t,n){"use strict";var r="default"in t?t.default:t;function o(){return(o=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var r in n)Object.prototype.hasOwnProperty.call(n,r)&&(e[r]=n[r])}return e}).apply(this,arguments)}function i(e,t){if(null==e)return{};var n,r,o={},i=Object.keys(e);for(r=0;r<i.length;r++)n=i[r],t.indexOf(n)>=0||(o[n]=e[n]);return o}function s(e,t){e.prototype=Object.create(t.prototype),e.prototype.constructor=e,e.__proto__=t}function a(e,t){return e(t={exports:{}},t.exports),t.exports
+!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("react"),require("react-dom")):"function"==typeof define&&define.amd?define(["exports","react","react-dom"],t):t((e=e||self).ReactTransitionGroup={},e.React,e.ReactDOM)}(this,(function(e,t,n){"use strict";var r="default"in t?t.default:t,o="default"in n?n.default:n;function i(){return(i=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var r in n)Object.prototype.hasOwnProperty.call(n,r)&&(e[r]=n[r])}return e}).apply(this,arguments)}function s(e,t){if(null==e)return{};var n,r,o={},i=Object.keys(e);for(r=0;r<i.length;r++)n=i[r],t.indexOf(n)>=0||(o[n]=e[n]);return o}function a(e,t){e.prototype=Object.create(t.prototype),e.prototype.constructor=e,e.__proto__=t}function c(e,t){return e(t={exports:{}},t.exports),t.exports
 /** @license React v16.13.1
    * react-is.production.min.js
    *
@@ -6,4 +6,4 @@
    *
    * This source code is licensed under the MIT license found in the
    * LICENSE file in the root directory of this source tree.
-   */}n=n&&Object.prototype.hasOwnProperty.call(n,"default")?n.default:n;var c="function"==typeof Symbol&&Symbol.for,l=c?Symbol.for("react.element"):60103,u=c?Symbol.for("react.portal"):60106,p=c?Symbol.for("react.fragment"):60107,d=c?Symbol.for("react.strict_mode"):60108,f=c?Symbol.for("react.profiler"):60114,h=c?Symbol.for("react.provider"):60109,m=c?Symbol.for("react.context"):60110,E=c?Symbol.for("react.async_mode"):60111,y=c?Symbol.for("react.concurrent_mode"):60111,x=c?Symbol.for("react.forward_ref"):60112,v=c?Symbol.for("react.suspense"):60113,g=c?Symbol.for("react.suspense_list"):60120,b=c?Symbol.for("react.memo"):60115,C=c?Symbol.for("react.lazy"):60116,S=c?Symbol.for("react.block"):60121,O=c?Symbol.for("react.fundamental"):60117,N=c?Symbol.for("react.responder"):60118,T=c?Symbol.for("react.scope"):60119;function k(e){if("object"==typeof e&&null!==e){var t=e.$$typeof;switch(t){case l:switch(e=e.type){case E:case y:case p:case f:case d:case v:return e;default:switch(e=e&&e.$$typeof){case m:case x:case C:case b:case h:return e;default:return t}}case u:return t}}}function P(e){return k(e)===y}var w={AsyncMode:E,ConcurrentMode:y,ContextConsumer:m,ContextProvider:h,Element:l,ForwardRef:x,Fragment:p,Lazy:C,Memo:b,Portal:u,Profiler:f,StrictMode:d,Suspense:v,isAsyncMode:function(e){return P(e)||k(e)===E},isConcurrentMode:P,isContextConsumer:function(e){return k(e)===m},isContextProvider:function(e){return k(e)===h},isElement:function(e){return"object"==typeof e&&null!==e&&e.$$typeof===l},isForwardRef:function(e){return k(e)===x},isFragment:function(e){return k(e)===p},isLazy:function(e){return k(e)===C},isMemo:function(e){return k(e)===b},isPortal:function(e){return k(e)===u},isProfiler:function(e){return k(e)===f},isStrictMode:function(e){return k(e)===d},isSuspense:function(e){return k(e)===v},isValidElementType:function(e){return"string"==typeof e||"function"==typeof e||e===p||e===y||e===f||e===d||e===v||e===g||"object"==typeof e&&null!==e&&(e.$$typeof===C||e.$$typeof===b||e.$$typeof===h||e.$$typeof===m||e.$$typeof===x||e.$$typeof===O||e.$$typeof===N||e.$$typeof===T||e.$$typeof===S)},typeOf:k},j=a((function(e,t){})),M=(j.AsyncMode,j.ConcurrentMode,j.ContextConsumer,j.ContextProvider,j.Element,j.ForwardRef,j.Fragment,j.Lazy,j.Memo,j.Portal,j.Profiler,j.StrictMode,j.Suspense,j.isAsyncMode,j.isConcurrentMode,j.isContextConsumer,j.isContextProvider,j.isElement,j.isForwardRef,j.isFragment,j.isLazy,j.isMemo,j.isPortal,j.isProfiler,j.isStrictMode,j.isSuspense,j.isValidElementType,j.typeOf,a((function(e){e.exports=w})),Object.getOwnPropertySymbols),R=Object.prototype.hasOwnProperty,$=Object.prototype.propertyIsEnumerable;function A(e){if(null==e)throw new TypeError("Object.assign cannot be called with null or undefined");return Object(e)}(function(){try{if(!Object.assign)return!1;var e=new String("abc");if(e[5]="de","5"===Object.getOwnPropertyNames(e)[0])return!1;for(var t={},n=0;n<10;n++)t["_"+String.fromCharCode(n)]=n;if("0123456789"!==Object.getOwnPropertyNames(t).map((function(e){return t[e]})).join(""))return!1;var r={};return"abcdefghijklmnopqrst".split("").forEach((function(e){r[e]=e})),"abcdefghijklmnopqrst"===Object.keys(Object.assign({},r)).join("")}catch(e){return!1}})()&&Object.assign,Function.call.bind(Object.prototype.hasOwnProperty);function L(){}function _(){}_.resetWarningCache=L;var D=a((function(e){e.exports=function(){function e(e,t,n,r,o,i){if("SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED"!==i){var s=new Error("Calling PropTypes validators directly is not supported by the `prop-types` package. Use PropTypes.checkPropTypes() to call them. Read more at http://fb.me/use-check-prop-types");throw s.name="Invariant Violation",s}}function t(){return e}e.isRequired=e;var n={array:e,bool:e,func:e,number:e,object:e,string:e,symbol:e,any:e,arrayOf:t,element:e,elementType:e,instanceOf:t,node:e,objectOf:t,oneOf:t,oneOfType:t,shape:t,exact:t,checkPropTypes:_,resetWarningCache:L};return n.PropTypes=n,n}()}));D.object,D.oneOfType,D.element,D.bool,D.func;function F(e,t){return e.replace(new RegExp("(^|\\s)"+t+"(?:\\s|$)","g"),"$1").replace(/\s+/g," ").replace(/^\s*|\s*$/g,"")}var V={disabled:!1},I=r.createContext(null),U=function(e){return e.scrollTop},q="entering",z="entered",W=function(e){function t(t,n){var r;r=e.call(this,t,n)||this;var o,i=n&&!n.isMounting?t.enter:t.appear;return r.appearStatus=null,t.in?i?(o="exited",r.appearStatus=q):o=z:o=t.unmountOnExit||t.mountOnEnter?"unmounted":"exited",r.state={status:o},r.nextCallback=null,r}s(t,e),t.getDerivedStateFromProps=function(e,t){return e.in&&"unmounted"===t.status?{status:"exited"}:null};var o=t.prototype;return o.componentDidMount=function(){this.updateStatus(!0,this.appearStatus)},o.componentDidUpdate=function(e){var t=null;if(e!==this.props){var n=this.state.status;this.props.in?n!==q&&n!==z&&(t=q):n!==q&&n!==z||(t="exiting")}this.updateStatus(!1,t)},o.componentWillUnmount=function(){this.cancelNextCallback()},o.getTimeouts=function(){var e,t,n,r=this.props.timeout;return e=t=n=r,null!=r&&"number"!=typeof r&&(e=r.exit,t=r.enter,n=void 0!==r.appear?r.appear:t),{exit:e,enter:t,appear:n}},o.updateStatus=function(e,t){if(void 0===e&&(e=!1),null!==t)if(this.cancelNextCallback(),t===q){if(this.props.unmountOnExit||this.props.mountOnEnter){var r=this.props.nodeRef?this.props.nodeRef.current:n.findDOMNode(this);r&&U(r)}this.performEnter(e)}else this.performExit();else this.props.unmountOnExit&&"exited"===this.state.status&&this.setState({status:"unmounted"})},o.performEnter=function(e){var t=this,r=this.props.enter,o=this.context?this.context.isMounting:e,i=this.props.nodeRef?[o]:[n.findDOMNode(this),o],s=i[0],a=i[1],c=this.getTimeouts(),l=o?c.appear:c.enter;!e&&!r||V.disabled?this.safeSetState({status:z},(function(){t.props.onEntered(s)})):(this.props.onEnter(s,a),this.safeSetState({status:q},(function(){t.props.onEntering(s,a),t.onTransitionEnd(l,(function(){t.safeSetState({status:z},(function(){t.props.onEntered(s,a)}))}))})))},o.performExit=function(){var e=this,t=this.props.exit,r=this.getTimeouts(),o=this.props.nodeRef?void 0:n.findDOMNode(this);t&&!V.disabled?(this.props.onExit(o),this.safeSetState({status:"exiting"},(function(){e.props.onExiting(o),e.onTransitionEnd(r.exit,(function(){e.safeSetState({status:"exited"},(function(){e.props.onExited(o)}))}))}))):this.safeSetState({status:"exited"},(function(){e.props.onExited(o)}))},o.cancelNextCallback=function(){null!==this.nextCallback&&(this.nextCallback.cancel(),this.nextCallback=null)},o.safeSetState=function(e,t){t=this.setNextCallback(t),this.setState(e,t)},o.setNextCallback=function(e){var t=this,n=!0;return this.nextCallback=function(r){n&&(n=!1,t.nextCallback=null,e(r))},this.nextCallback.cancel=function(){n=!1},this.nextCallback},o.onTransitionEnd=function(e,t){this.setNextCallback(t);var r=this.props.nodeRef?this.props.nodeRef.current:n.findDOMNode(this),o=null==e&&!this.props.addEndListener;if(r&&!o){if(this.props.addEndListener){var i=this.props.nodeRef?[this.nextCallback]:[r,this.nextCallback],s=i[0],a=i[1];this.props.addEndListener(s,a)}null!=e&&setTimeout(this.nextCallback,e)}else setTimeout(this.nextCallback,0)},o.render=function(){var e=this.state.status;if("unmounted"===e)return null;var t=this.props,n=t.children,o=(t.in,t.mountOnEnter,t.unmountOnExit,t.appear,t.enter,t.exit,t.timeout,t.addEndListener,t.onEnter,t.onEntering,t.onEntered,t.onExit,t.onExiting,t.onExited,t.nodeRef,i(t,["children","in","mountOnEnter","unmountOnExit","appear","enter","exit","timeout","addEndListener","onEnter","onEntering","onEntered","onExit","onExiting","onExited","nodeRef"]));return r.createElement(I.Provider,{value:null},"function"==typeof n?n(e,o):r.cloneElement(r.Children.only(n),o))},t}(r.Component);function G(){}W.contextType=I,W.propTypes={},W.defaultProps={in:!1,mountOnEnter:!1,unmountOnExit:!1,appear:!1,enter:!0,exit:!0,onEnter:G,onEntering:G,onEntered:G,onExit:G,onExiting:G,onExited:G},W.UNMOUNTED="unmounted",W.EXITED="exited",W.ENTERING=q,W.ENTERED=z,W.EXITING="exiting";var X=function(e,t){return e&&t&&t.split(" ").forEach((function(t){return r=t,void((n=e).classList?n.classList.add(r):function(e,t){return e.classList?!!t&&e.classList.contains(t):-1!==(" "+(e.className.baseVal||e.className)+" ").indexOf(" "+t+" ")}(n,r)||("string"==typeof n.className?n.className=n.className+" "+r:n.setAttribute("class",(n.className&&n.className.baseVal||"")+" "+r)));var n,r}))},B=function(e,t){return e&&t&&t.split(" ").forEach((function(t){return r=t,void((n=e).classList?n.classList.remove(r):"string"==typeof n.className?n.className=F(n.className,r):n.setAttribute("class",F(n.className&&n.className.baseVal||"",r)));var n,r}))},H=function(e){function t(){for(var t,n=arguments.length,r=new Array(n),o=0;o<n;o++)r[o]=arguments[o];return(t=e.call.apply(e,[this].concat(r))||this).appliedClasses={appear:{},enter:{},exit:{}},t.onEnter=function(e,n){var r=t.resolveArguments(e,n),o=r[0],i=r[1];t.removeClasses(o,"exit"),t.addClass(o,i?"appear":"enter","base"),t.props.onEnter&&t.props.onEnter(e,n)},t.onEntering=function(e,n){var r=t.resolveArguments(e,n),o=r[0],i=r[1]?"appear":"enter";t.addClass(o,i,"active"),t.props.onEntering&&t.props.onEntering(e,n)},t.onEntered=function(e,n){var r=t.resolveArguments(e,n),o=r[0],i=r[1]?"appear":"enter";t.removeClasses(o,i),t.addClass(o,i,"done"),t.props.onEntered&&t.props.onEntered(e,n)},t.onExit=function(e){var n=t.resolveArguments(e)[0];t.removeClasses(n,"appear"),t.removeClasses(n,"enter"),t.addClass(n,"exit","base"),t.props.onExit&&t.props.onExit(e)},t.onExiting=function(e){var n=t.resolveArguments(e)[0];t.addClass(n,"exit","active"),t.props.onExiting&&t.props.onExiting(e)},t.onExited=function(e){var n=t.resolveArguments(e)[0];t.removeClasses(n,"exit"),t.addClass(n,"exit","done"),t.props.onExited&&t.props.onExited(e)},t.resolveArguments=function(e,n){return t.props.nodeRef?[t.props.nodeRef.current,e]:[e,n]},t.getClassNames=function(e){var n=t.props.classNames,r="string"==typeof n,o=r?""+(r&&n?n+"-":"")+e:n[e];return{baseClassName:o,activeClassName:r?o+"-active":n[e+"Active"],doneClassName:r?o+"-done":n[e+"Done"]}},t}s(t,e);var n=t.prototype;return n.addClass=function(e,t,n){var r=this.getClassNames(t)[n+"ClassName"],o=this.getClassNames("enter").doneClassName;"appear"===t&&"done"===n&&o&&(r+=" "+o),"active"===n&&e&&U(e),r&&(this.appliedClasses[t][n]=r,X(e,r))},n.removeClasses=function(e,t){var n=this.appliedClasses[t],r=n.base,o=n.active,i=n.done;this.appliedClasses[t]={},r&&B(e,r),o&&B(e,o),i&&B(e,i)},n.render=function(){var e=this.props,t=(e.classNames,i(e,["classNames"]));return r.createElement(W,o({},t,{onEnter:this.onEnter,onEntered:this.onEntered,onEntering:this.onEntering,onExit:this.onExit,onExiting:this.onExiting,onExited:this.onExited}))},t}(r.Component);function Y(e,n){var r=Object.create(null);return e&&t.Children.map(e,(function(e){return e})).forEach((function(e){r[e.key]=function(e){return n&&t.isValidElement(e)?n(e):e}(e)})),r}function J(e,t,n){return null!=n[t]?n[t]:e.props[t]}function K(e,n,r){var o=Y(e.children),i=function(e,t){function n(n){return n in t?t[n]:e[n]}e=e||{},t=t||{};var r,o=Object.create(null),i=[];for(var s in e)s in t?i.length&&(o[s]=i,i=[]):i.push(s);var a={};for(var c in t){if(o[c])for(r=0;r<o[c].length;r++){var l=o[c][r];a[o[c][r]]=n(l)}a[c]=n(c)}for(r=0;r<i.length;r++)a[i[r]]=n(i[r]);return a}(n,o);return Object.keys(i).forEach((function(s){var a=i[s];if(t.isValidElement(a)){var c=s in n,l=s in o,u=n[s],p=t.isValidElement(u)&&!u.props.in;!l||c&&!p?l||!c||p?l&&c&&t.isValidElement(u)&&(i[s]=t.cloneElement(a,{onExited:r.bind(null,a),in:u.props.in,exit:J(a,"exit",e),enter:J(a,"enter",e)})):i[s]=t.cloneElement(a,{in:!1}):i[s]=t.cloneElement(a,{onExited:r.bind(null,a),in:!0,exit:J(a,"exit",e),enter:J(a,"enter",e)})}})),i}H.defaultProps={classNames:""},H.propTypes={};var Q=Object.values||function(e){return Object.keys(e).map((function(t){return e[t]}))},Z=function(e){function n(t,n){var r,o=(r=e.call(this,t,n)||this).handleExited.bind(function(e){if(void 0===e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e}(r));return r.state={contextValue:{isMounting:!0},handleExited:o,firstRender:!0},r}s(n,e);var a=n.prototype;return a.componentDidMount=function(){this.mounted=!0,this.setState({contextValue:{isMounting:!1}})},a.componentWillUnmount=function(){this.mounted=!1},n.getDerivedStateFromProps=function(e,n){var r,o,i=n.children,s=n.handleExited;return{children:n.firstRender?(r=e,o=s,Y(r.children,(function(e){return t.cloneElement(e,{onExited:o.bind(null,e),in:!0,appear:J(e,"appear",r),enter:J(e,"enter",r),exit:J(e,"exit",r)})}))):K(e,i,s),firstRender:!1}},a.handleExited=function(e,t){var n=Y(this.props.children);e.key in n||(e.props.onExited&&e.props.onExited(t),this.mounted&&this.setState((function(t){var n=o({},t.children);return delete n[e.key],{children:n}})))},a.render=function(){var e=this.props,t=e.component,n=e.childFactory,o=i(e,["component","childFactory"]),s=this.state.contextValue,a=Q(this.state.children).map(n);return delete o.appear,delete o.enter,delete o.exit,null===t?r.createElement(I.Provider,{value:s},a):r.createElement(I.Provider,{value:s},r.createElement(t,o,a))},n}(r.Component);Z.propTypes={},Z.defaultProps={component:"div",childFactory:function(e){return e}};var ee,te,ne=function(e){function t(){for(var t,n=arguments.length,r=new Array(n),o=0;o<n;o++)r[o]=arguments[o];return(t=e.call.apply(e,[this].concat(r))||this).handleEnter=function(){for(var e=arguments.length,n=new Array(e),r=0;r<e;r++)n[r]=arguments[r];return t.handleLifecycle("onEnter",0,n)},t.handleEntering=function(){for(var e=arguments.length,n=new Array(e),r=0;r<e;r++)n[r]=arguments[r];return t.handleLifecycle("onEntering",0,n)},t.handleEntered=function(){for(var e=arguments.length,n=new Array(e),r=0;r<e;r++)n[r]=arguments[r];return t.handleLifecycle("onEntered",0,n)},t.handleExit=function(){for(var e=arguments.length,n=new Array(e),r=0;r<e;r++)n[r]=arguments[r];return t.handleLifecycle("onExit",1,n)},t.handleExiting=function(){for(var e=arguments.length,n=new Array(e),r=0;r<e;r++)n[r]=arguments[r];return t.handleLifecycle("onExiting",1,n)},t.handleExited=function(){for(var e=arguments.length,n=new Array(e),r=0;r<e;r++)n[r]=arguments[r];return t.handleLifecycle("onExited",1,n)},t}s(t,e);var o=t.prototype;return o.handleLifecycle=function(e,t,o){var i,s=this.props.children,a=r.Children.toArray(s)[t];if(a.props[e]&&(i=a.props)[e].apply(i,o),this.props[e]){var c=a.props.nodeRef?void 0:n.findDOMNode(this);this.props[e](c)}},o.render=function(){var e=this.props,t=e.children,n=e.in,o=i(e,["children","in"]),s=r.Children.toArray(t),a=s[0],c=s[1];return delete o.onEnter,delete o.onEntering,delete o.onEntered,delete o.onExit,delete o.onExiting,delete o.onExited,r.createElement(Z,o,n?r.cloneElement(a,{key:"first",onEnter:this.handleEnter,onEntering:this.handleEntering,onEntered:this.handleEntered}):r.cloneElement(c,{key:"second",onEnter:this.handleExit,onEntering:this.handleExiting,onEntered:this.handleExited}))},t}(r.Component);ne.propTypes={};var re="out-in",oe="in-out",ie=function(e,t,n){return function(){var r;e.props[t]&&(r=e.props)[t].apply(r,arguments),n()}},se=((ee={})[re]=function(e){var t=e.current,n=e.changeState;return r.cloneElement(t,{in:!1,onExited:ie(t,"onExited",(function(){n(q,null)}))})},ee[oe]=function(e){var t=e.current,n=e.changeState,o=e.children;return[t,r.cloneElement(o,{in:!0,onEntered:ie(o,"onEntered",(function(){n(q)}))})]},ee),ae=((te={})[re]=function(e){var t=e.children,n=e.changeState;return r.cloneElement(t,{in:!0,onEntered:ie(t,"onEntered",(function(){n(z,r.cloneElement(t,{in:!0}))}))})},te[oe]=function(e){var t=e.current,n=e.children,o=e.changeState;return[r.cloneElement(t,{in:!1,onExited:ie(t,"onExited",(function(){o(z,r.cloneElement(n,{in:!0}))}))}),r.cloneElement(n,{in:!0})]},te),ce=function(e){function t(){for(var t,n=arguments.length,r=new Array(n),o=0;o<n;o++)r[o]=arguments[o];return(t=e.call.apply(e,[this].concat(r))||this).state={status:z,current:null},t.appeared=!1,t.changeState=function(e,n){void 0===n&&(n=t.state.current),t.setState({status:e,current:n})},t}s(t,e);var n=t.prototype;return n.componentDidMount=function(){this.appeared=!0},t.getDerivedStateFromProps=function(e,t){return null==e.children?{current:null}:t.status===q&&e.mode===oe?{status:q}:!t.current||(n=t.current,o=e.children,n===o||r.isValidElement(n)&&r.isValidElement(o)&&null!=n.key&&n.key===o.key)?{current:r.cloneElement(e.children,{in:!0})}:{status:"exiting"};var n,o},n.render=function(){var e,t=this.props,n=t.children,o=t.mode,i=this.state,s=i.status,a=i.current,c={children:n,current:a,changeState:this.changeState,status:s};switch(s){case q:e=ae[o](c);break;case"exiting":e=se[o](c);break;case z:e=a}return r.createElement(I.Provider,{value:{isMounting:!this.appeared}},e)},t}(r.Component);ce.propTypes={},ce.defaultProps={mode:re},e.CSSTransition=H,e.ReplaceTransition=ne,e.SwitchTransition=ce,e.Transition=W,e.TransitionGroup=Z,e.config=V,Object.defineProperty(e,"__esModule",{value:!0})}));
+   */}var l="function"==typeof Symbol&&Symbol.for,u=l?Symbol.for("react.element"):60103,p=l?Symbol.for("react.portal"):60106,d=l?Symbol.for("react.fragment"):60107,f=l?Symbol.for("react.strict_mode"):60108,h=l?Symbol.for("react.profiler"):60114,m=l?Symbol.for("react.provider"):60109,E=l?Symbol.for("react.context"):60110,y=l?Symbol.for("react.async_mode"):60111,x=l?Symbol.for("react.concurrent_mode"):60111,v=l?Symbol.for("react.forward_ref"):60112,g=l?Symbol.for("react.suspense"):60113,b=l?Symbol.for("react.suspense_list"):60120,C=l?Symbol.for("react.memo"):60115,S=l?Symbol.for("react.lazy"):60116,O=l?Symbol.for("react.block"):60121,N=l?Symbol.for("react.fundamental"):60117,T=l?Symbol.for("react.responder"):60118,k=l?Symbol.for("react.scope"):60119;function P(e){if("object"==typeof e&&null!==e){var t=e.$$typeof;switch(t){case u:switch(e=e.type){case y:case x:case d:case h:case f:case g:return e;default:switch(e=e&&e.$$typeof){case E:case v:case S:case C:case m:return e;default:return t}}case p:return t}}}function w(e){return P(e)===x}var j={AsyncMode:y,ConcurrentMode:x,ContextConsumer:E,ContextProvider:m,Element:u,ForwardRef:v,Fragment:d,Lazy:S,Memo:C,Portal:p,Profiler:h,StrictMode:f,Suspense:g,isAsyncMode:function(e){return w(e)||P(e)===y},isConcurrentMode:w,isContextConsumer:function(e){return P(e)===E},isContextProvider:function(e){return P(e)===m},isElement:function(e){return"object"==typeof e&&null!==e&&e.$$typeof===u},isForwardRef:function(e){return P(e)===v},isFragment:function(e){return P(e)===d},isLazy:function(e){return P(e)===S},isMemo:function(e){return P(e)===C},isPortal:function(e){return P(e)===p},isProfiler:function(e){return P(e)===h},isStrictMode:function(e){return P(e)===f},isSuspense:function(e){return P(e)===g},isValidElementType:function(e){return"string"==typeof e||"function"==typeof e||e===d||e===x||e===h||e===f||e===g||e===b||"object"==typeof e&&null!==e&&(e.$$typeof===S||e.$$typeof===C||e.$$typeof===m||e.$$typeof===E||e.$$typeof===v||e.$$typeof===N||e.$$typeof===T||e.$$typeof===k||e.$$typeof===O)},typeOf:P},M=c((function(e,t){})),R=(M.AsyncMode,M.ConcurrentMode,M.ContextConsumer,M.ContextProvider,M.Element,M.ForwardRef,M.Fragment,M.Lazy,M.Memo,M.Portal,M.Profiler,M.StrictMode,M.Suspense,M.isAsyncMode,M.isConcurrentMode,M.isContextConsumer,M.isContextProvider,M.isElement,M.isForwardRef,M.isFragment,M.isLazy,M.isMemo,M.isPortal,M.isProfiler,M.isStrictMode,M.isSuspense,M.isValidElementType,M.typeOf,c((function(e){e.exports=j})),Object.getOwnPropertySymbols),$=Object.prototype.hasOwnProperty,A=Object.prototype.propertyIsEnumerable;function L(e){if(null==e)throw new TypeError("Object.assign cannot be called with null or undefined");return Object(e)}(function(){try{if(!Object.assign)return!1;var e=new String("abc");if(e[5]="de","5"===Object.getOwnPropertyNames(e)[0])return!1;for(var t={},n=0;n<10;n++)t["_"+String.fromCharCode(n)]=n;if("0123456789"!==Object.getOwnPropertyNames(t).map((function(e){return t[e]})).join(""))return!1;var r={};return"abcdefghijklmnopqrst".split("").forEach((function(e){r[e]=e})),"abcdefghijklmnopqrst"===Object.keys(Object.assign({},r)).join("")}catch(e){return!1}})()&&Object.assign,Function.call.bind(Object.prototype.hasOwnProperty);function _(){}function D(){}D.resetWarningCache=_;var F=c((function(e){e.exports=function(){function e(e,t,n,r,o,i){if("SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED"!==i){var s=new Error("Calling PropTypes validators directly is not supported by the `prop-types` package. Use PropTypes.checkPropTypes() to call them. Read more at http://fb.me/use-check-prop-types");throw s.name="Invariant Violation",s}}function t(){return e}e.isRequired=e;var n={array:e,bool:e,func:e,number:e,object:e,string:e,symbol:e,any:e,arrayOf:t,element:e,elementType:e,instanceOf:t,node:e,objectOf:t,oneOf:t,oneOfType:t,shape:t,exact:t,checkPropTypes:D,resetWarningCache:_};return n.PropTypes=n,n}()}));F.object,F.oneOfType,F.element,F.bool,F.func;function V(e,t){return e.replace(new RegExp("(^|\\s)"+t+"(?:\\s|$)","g"),"$1").replace(/\s+/g," ").replace(/^\s*|\s*$/g,"")}var I={disabled:!1},U=r.createContext(null),q=function(e){return e.scrollTop},z="entering",W="entered",G=function(e){function t(t,n){var r;r=e.call(this,t,n)||this;var o,i=n&&!n.isMounting?t.enter:t.appear;return r.appearStatus=null,t.in?i?(o="exited",r.appearStatus=z):o=W:o=t.unmountOnExit||t.mountOnEnter?"unmounted":"exited",r.state={status:o},r.nextCallback=null,r}a(t,e),t.getDerivedStateFromProps=function(e,t){return e.in&&"unmounted"===t.status?{status:"exited"}:null};var i=t.prototype;return i.componentDidMount=function(){this.updateStatus(!0,this.appearStatus)},i.componentDidUpdate=function(e){var t=null;if(e!==this.props){var n=this.state.status;this.props.in?n!==z&&n!==W&&(t=z):n!==z&&n!==W||(t="exiting")}this.updateStatus(!1,t)},i.componentWillUnmount=function(){this.cancelNextCallback()},i.getTimeouts=function(){var e,t,n,r=this.props.timeout;return e=t=n=r,null!=r&&"number"!=typeof r&&(e=r.exit,t=r.enter,n=void 0!==r.appear?r.appear:t),{exit:e,enter:t,appear:n}},i.updateStatus=function(e,t){if(void 0===e&&(e=!1),null!==t)if(this.cancelNextCallback(),t===z){if(this.props.unmountOnExit||this.props.mountOnEnter){var n=this.props.nodeRef?this.props.nodeRef.current:o.findDOMNode(this);n&&q(n)}this.performEnter(e)}else this.performExit();else this.props.unmountOnExit&&"exited"===this.state.status&&this.setState({status:"unmounted"})},i.performEnter=function(e){var t=this,n=this.props.enter,r=this.context?this.context.isMounting:e,i=this.props.nodeRef?[r]:[o.findDOMNode(this),r],s=i[0],a=i[1],c=this.getTimeouts(),l=r?c.appear:c.enter;!e&&!n||I.disabled?this.safeSetState({status:W},(function(){t.props.onEntered(s)})):(this.props.onEnter(s,a),this.safeSetState({status:z},(function(){t.props.onEntering(s,a),t.onTransitionEnd(l,(function(){t.safeSetState({status:W},(function(){t.props.onEntered(s,a)}))}))})))},i.performExit=function(){var e=this,t=this.props.exit,n=this.getTimeouts(),r=this.props.nodeRef?void 0:o.findDOMNode(this);t&&!I.disabled?(this.props.onExit(r),this.safeSetState({status:"exiting"},(function(){e.props.onExiting(r),e.onTransitionEnd(n.exit,(function(){e.safeSetState({status:"exited"},(function(){e.props.onExited(r)}))}))}))):this.safeSetState({status:"exited"},(function(){e.props.onExited(r)}))},i.cancelNextCallback=function(){null!==this.nextCallback&&(this.nextCallback.cancel(),this.nextCallback=null)},i.safeSetState=function(e,t){var r=this;"exited"===(null==e?void 0:e.status)?n.flushSync((function(){r.setState(e,t)})):this.setState(e,t)},i.setNextCallback=function(e){var t=this,n=!0;return this.nextCallback=function(r){n&&(n=!1,t.nextCallback=null,e(r))},this.nextCallback.cancel=function(){n=!1},this.nextCallback},i.onTransitionEnd=function(e,t){this.setNextCallback(t);var n=this.props.nodeRef?this.props.nodeRef.current:o.findDOMNode(this),r=null==e&&!this.props.addEndListener;if(n&&!r){if(this.props.addEndListener){var i=this.props.nodeRef?[this.nextCallback]:[n,this.nextCallback],s=i[0],a=i[1];this.props.addEndListener(s,a)}null!=e&&setTimeout(this.nextCallback,e)}else setTimeout(this.nextCallback,0)},i.render=function(){var e=this.state.status;if("unmounted"===e)return null;var t=this.props,n=t.children,o=(t.in,t.mountOnEnter,t.unmountOnExit,t.appear,t.enter,t.exit,t.timeout,t.addEndListener,t.onEnter,t.onEntering,t.onEntered,t.onExit,t.onExiting,t.onExited,t.nodeRef,s(t,["children","in","mountOnEnter","unmountOnExit","appear","enter","exit","timeout","addEndListener","onEnter","onEntering","onEntered","onExit","onExiting","onExited","nodeRef"]));return r.createElement(U.Provider,{value:null},"function"==typeof n?n(e,o):r.cloneElement(r.Children.only(n),o))},t}(r.Component);function X(){}G.contextType=U,G.propTypes={},G.defaultProps={in:!1,mountOnEnter:!1,unmountOnExit:!1,appear:!1,enter:!0,exit:!0,onEnter:X,onEntering:X,onEntered:X,onExit:X,onExiting:X,onExited:X},G.UNMOUNTED="unmounted",G.EXITED="exited",G.ENTERING=z,G.ENTERED=W,G.EXITING="exiting";var B=function(e,t){return e&&t&&t.split(" ").forEach((function(t){return r=t,void((n=e).classList?n.classList.add(r):function(e,t){return e.classList?!!t&&e.classList.contains(t):-1!==(" "+(e.className.baseVal||e.className)+" ").indexOf(" "+t+" ")}(n,r)||("string"==typeof n.className?n.className=n.className+" "+r:n.setAttribute("class",(n.className&&n.className.baseVal||"")+" "+r)));var n,r}))},H=function(e,t){return e&&t&&t.split(" ").forEach((function(t){return r=t,void((n=e).classList?n.classList.remove(r):"string"==typeof n.className?n.className=V(n.className,r):n.setAttribute("class",V(n.className&&n.className.baseVal||"",r)));var n,r}))},Y=function(e){function t(){for(var t,n=arguments.length,r=new Array(n),o=0;o<n;o++)r[o]=arguments[o];return(t=e.call.apply(e,[this].concat(r))||this).appliedClasses={appear:{},enter:{},exit:{}},t.onEnter=function(e,n){var r=t.resolveArguments(e,n),o=r[0],i=r[1];t.removeClasses(o,"exit"),t.addClass(o,i?"appear":"enter","base"),t.props.onEnter&&t.props.onEnter(e,n)},t.onEntering=function(e,n){var r=t.resolveArguments(e,n),o=r[0],i=r[1]?"appear":"enter";t.addClass(o,i,"active"),t.props.onEntering&&t.props.onEntering(e,n)},t.onEntered=function(e,n){var r=t.resolveArguments(e,n),o=r[0],i=r[1]?"appear":"enter";t.removeClasses(o,i),t.addClass(o,i,"done"),t.props.onEntered&&t.props.onEntered(e,n)},t.onExit=function(e){var n=t.resolveArguments(e)[0];t.removeClasses(n,"appear"),t.removeClasses(n,"enter"),t.addClass(n,"exit","base"),t.props.onExit&&t.props.onExit(e)},t.onExiting=function(e){var n=t.resolveArguments(e)[0];t.addClass(n,"exit","active"),t.props.onExiting&&t.props.onExiting(e)},t.onExited=function(e){var n=t.resolveArguments(e)[0];t.removeClasses(n,"exit"),t.addClass(n,"exit","done"),t.props.onExited&&t.props.onExited(e)},t.resolveArguments=function(e,n){return t.props.nodeRef?[t.props.nodeRef.current,e]:[e,n]},t.getClassNames=function(e){var n=t.props.classNames,r="string"==typeof n,o=r?""+(r&&n?n+"-":"")+e:n[e];return{baseClassName:o,activeClassName:r?o+"-active":n[e+"Active"],doneClassName:r?o+"-done":n[e+"Done"]}},t}a(t,e);var n=t.prototype;return n.addClass=function(e,t,n){var r=this.getClassNames(t)[n+"ClassName"],o=this.getClassNames("enter").doneClassName;"appear"===t&&"done"===n&&o&&(r+=" "+o),"active"===n&&e&&q(e),r&&(this.appliedClasses[t][n]=r,B(e,r))},n.removeClasses=function(e,t){var n=this.appliedClasses[t],r=n.base,o=n.active,i=n.done;this.appliedClasses[t]={},r&&H(e,r),o&&H(e,o),i&&H(e,i)},n.render=function(){var e=this.props,t=(e.classNames,s(e,["classNames"]));return r.createElement(G,i({},t,{onEnter:this.onEnter,onEntered:this.onEntered,onEntering:this.onEntering,onExit:this.onExit,onExiting:this.onExiting,onExited:this.onExited}))},t}(r.Component);function J(e,n){var r=Object.create(null);return e&&t.Children.map(e,(function(e){return e})).forEach((function(e){r[e.key]=function(e){return n&&t.isValidElement(e)?n(e):e}(e)})),r}function K(e,t,n){return null!=n[t]?n[t]:e.props[t]}function Q(e,n,r){var o=J(e.children),i=function(e,t){function n(n){return n in t?t[n]:e[n]}e=e||{},t=t||{};var r,o=Object.create(null),i=[];for(var s in e)s in t?i.length&&(o[s]=i,i=[]):i.push(s);var a={};for(var c in t){if(o[c])for(r=0;r<o[c].length;r++){var l=o[c][r];a[o[c][r]]=n(l)}a[c]=n(c)}for(r=0;r<i.length;r++)a[i[r]]=n(i[r]);return a}(n,o);return Object.keys(i).forEach((function(s){var a=i[s];if(t.isValidElement(a)){var c=s in n,l=s in o,u=n[s],p=t.isValidElement(u)&&!u.props.in;!l||c&&!p?l||!c||p?l&&c&&t.isValidElement(u)&&(i[s]=t.cloneElement(a,{onExited:r.bind(null,a),in:u.props.in,exit:K(a,"exit",e),enter:K(a,"enter",e)})):i[s]=t.cloneElement(a,{in:!1}):i[s]=t.cloneElement(a,{onExited:r.bind(null,a),in:!0,exit:K(a,"exit",e),enter:K(a,"enter",e)})}})),i}Y.defaultProps={classNames:""},Y.propTypes={};var Z=Object.values||function(e){return Object.keys(e).map((function(t){return e[t]}))},ee=function(e){function n(t,n){var r,o=(r=e.call(this,t,n)||this).handleExited.bind(function(e){if(void 0===e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e}(r));return r.state={contextValue:{isMounting:!0},handleExited:o,firstRender:!0},r}a(n,e);var o=n.prototype;return o.componentDidMount=function(){this.mounted=!0,this.setState({contextValue:{isMounting:!1}})},o.componentWillUnmount=function(){this.mounted=!1},n.getDerivedStateFromProps=function(e,n){var r,o,i=n.children,s=n.handleExited;return{children:n.firstRender?(r=e,o=s,J(r.children,(function(e){return t.cloneElement(e,{onExited:o.bind(null,e),in:!0,appear:K(e,"appear",r),enter:K(e,"enter",r),exit:K(e,"exit",r)})}))):Q(e,i,s),firstRender:!1}},o.handleExited=function(e,t){var n=J(this.props.children);e.key in n||(e.props.onExited&&e.props.onExited(t),this.mounted&&this.setState((function(t){var n=i({},t.children);return delete n[e.key],{children:n}})))},o.render=function(){var e=this.props,t=e.component,n=e.childFactory,o=s(e,["component","childFactory"]),i=this.state.contextValue,a=Z(this.state.children).map(n);return delete o.appear,delete o.enter,delete o.exit,null===t?r.createElement(U.Provider,{value:i},a):r.createElement(U.Provider,{value:i},r.createElement(t,o,a))},n}(r.Component);ee.propTypes={},ee.defaultProps={component:"div",childFactory:function(e){return e}};var te,ne,re=function(e){function t(){for(var t,n=arguments.length,r=new Array(n),o=0;o<n;o++)r[o]=arguments[o];return(t=e.call.apply(e,[this].concat(r))||this).handleEnter=function(){for(var e=arguments.length,n=new Array(e),r=0;r<e;r++)n[r]=arguments[r];return t.handleLifecycle("onEnter",0,n)},t.handleEntering=function(){for(var e=arguments.length,n=new Array(e),r=0;r<e;r++)n[r]=arguments[r];return t.handleLifecycle("onEntering",0,n)},t.handleEntered=function(){for(var e=arguments.length,n=new Array(e),r=0;r<e;r++)n[r]=arguments[r];return t.handleLifecycle("onEntered",0,n)},t.handleExit=function(){for(var e=arguments.length,n=new Array(e),r=0;r<e;r++)n[r]=arguments[r];return t.handleLifecycle("onExit",1,n)},t.handleExiting=function(){for(var e=arguments.length,n=new Array(e),r=0;r<e;r++)n[r]=arguments[r];return t.handleLifecycle("onExiting",1,n)},t.handleExited=function(){for(var e=arguments.length,n=new Array(e),r=0;r<e;r++)n[r]=arguments[r];return t.handleLifecycle("onExited",1,n)},t}a(t,e);var n=t.prototype;return n.handleLifecycle=function(e,t,n){var i,s=this.props.children,a=r.Children.toArray(s)[t];if(a.props[e]&&(i=a.props)[e].apply(i,n),this.props[e]){var c=a.props.nodeRef?void 0:o.findDOMNode(this);this.props[e](c)}},n.render=function(){var e=this.props,t=e.children,n=e.in,o=s(e,["children","in"]),i=r.Children.toArray(t),a=i[0],c=i[1];return delete o.onEnter,delete o.onEntering,delete o.onEntered,delete o.onExit,delete o.onExiting,delete o.onExited,r.createElement(ee,o,n?r.cloneElement(a,{key:"first",onEnter:this.handleEnter,onEntering:this.handleEntering,onEntered:this.handleEntered}):r.cloneElement(c,{key:"second",onEnter:this.handleExit,onEntering:this.handleExiting,onEntered:this.handleExited}))},t}(r.Component);re.propTypes={};var oe="out-in",ie="in-out",se=function(e,t,n){return function(){var r;e.props[t]&&(r=e.props)[t].apply(r,arguments),n()}},ae=((te={})[oe]=function(e){var t=e.current,n=e.changeState;return r.cloneElement(t,{in:!1,onExited:se(t,"onExited",(function(){n(z,null)}))})},te[ie]=function(e){var t=e.current,n=e.changeState,o=e.children;return[t,r.cloneElement(o,{in:!0,onEntered:se(o,"onEntered",(function(){n(z)}))})]},te),ce=((ne={})[oe]=function(e){var t=e.children,n=e.changeState;return r.cloneElement(t,{in:!0,onEntered:se(t,"onEntered",(function(){n(W,r.cloneElement(t,{in:!0}))}))})},ne[ie]=function(e){var t=e.current,n=e.children,o=e.changeState;return[r.cloneElement(t,{in:!1,onExited:se(t,"onExited",(function(){o(W,r.cloneElement(n,{in:!0}))}))}),r.cloneElement(n,{in:!0})]},ne),le=function(e){function t(){for(var t,n=arguments.length,r=new Array(n),o=0;o<n;o++)r[o]=arguments[o];return(t=e.call.apply(e,[this].concat(r))||this).state={status:W,current:null},t.appeared=!1,t.changeState=function(e,n){void 0===n&&(n=t.state.current),t.setState({status:e,current:n})},t}a(t,e);var n=t.prototype;return n.componentDidMount=function(){this.appeared=!0},t.getDerivedStateFromProps=function(e,t){return null==e.children?{current:null}:t.status===z&&e.mode===ie?{status:z}:!t.current||(n=t.current,o=e.children,n===o||r.isValidElement(n)&&r.isValidElement(o)&&null!=n.key&&n.key===o.key)?{current:r.cloneElement(e.children,{in:!0})}:{status:"exiting"};var n,o},n.render=function(){var e,t=this.props,n=t.children,o=t.mode,i=this.state,s=i.status,a=i.current,c={children:n,current:a,changeState:this.changeState,status:s};switch(s){case z:e=ce[o](c);break;case"exiting":e=ae[o](c);break;case W:e=a}return r.createElement(U.Provider,{value:{isMounting:!this.appeared}},e)},t}(r.Component);le.propTypes={},le.defaultProps={mode:oe},e.CSSTransition=Y,e.ReplaceTransition=re,e.SwitchTransition=le,e.Transition=G,e.TransitionGroup=ee,e.config=I,Object.defineProperty(e,"__esModule",{value:!0})}));
diff --git a/esm/CSSTransition.js b/esm/CSSTransition.js
index fb43f53a5bd8354dfe9c3d6437c5f2c89227b628..72482dbd21dfc739fca989c65f4f373e9040248b 100644
--- a/esm/CSSTransition.js
+++ b/esm/CSSTransition.js
@@ -36,10 +36,11 @@ var removeClass = function removeClass(node, classes) {
  * ```jsx
  * function App() {
  *   const [inProp, setInProp] = useState(false);
+ *   const nodeRef = useRef(null);
  *   return (
  *     <div>
- *       <CSSTransition in={inProp} timeout={200} classNames="my-node">
- *         <div>
+ *       <CSSTransition nodeRef={nodeRef} in={inProp} timeout={200} classNames="my-node">
+ *         <div ref={nodeRef}>
  *           {"I'll receive my-node-* classes"}
  *         </div>
  *       </CSSTransition>
@@ -354,7 +355,7 @@ CSSTransition.propTypes = process.env.NODE_ENV !== "production" ? _extends({}, T
    * A `<Transition>` callback fired immediately after the 'enter' or 'appear' class is
    * applied.
    *
-   * **Note**: when `nodeRef` prop is passed, `node` is not passed.
+   * **Note**: when `nodeRef` prop is passed, `node` is not passed, so `isAppearing` is being passed as the first argument.
    *
    * @type Function(node: HtmlElement, isAppearing: bool)
    */
@@ -364,7 +365,7 @@ CSSTransition.propTypes = process.env.NODE_ENV !== "production" ? _extends({}, T
    * A `<Transition>` callback fired immediately after the 'enter-active' or
    * 'appear-active' class is applied.
    *
-   * **Note**: when `nodeRef` prop is passed, `node` is not passed.
+   * **Note**: when `nodeRef` prop is passed, `node` is not passed, so `isAppearing` is being passed as the first argument.
    *
    * @type Function(node: HtmlElement, isAppearing: bool)
    */
@@ -374,7 +375,7 @@ CSSTransition.propTypes = process.env.NODE_ENV !== "production" ? _extends({}, T
    * A `<Transition>` callback fired immediately after the 'enter' or
    * 'appear' classes are **removed** and the `done` class is added to the DOM node.
    *
-   * **Note**: when `nodeRef` prop is passed, `node` is not passed.
+   * **Note**: when `nodeRef` prop is passed, `node` is not passed, so `isAppearing` is being passed as the first argument.
    *
    * @type Function(node: HtmlElement, isAppearing: bool)
    */
diff --git a/esm/SwitchTransition.js b/esm/SwitchTransition.js
index 8ba2e9e38f7175d0f619072fc77b8ab5403e4c48..111e6eeec7f4d8efdc72b9b21edf359afb119b95 100644
--- a/esm/SwitchTransition.js
+++ b/esm/SwitchTransition.js
@@ -99,14 +99,18 @@ var enterRenders = (_enterRenders = {}, _enterRenders[modes.out] = function (_re
  * ```jsx
  * function App() {
  *  const [state, setState] = useState(false);
+ *  const helloRef = useRef(null);
+ *  const goodbyeRef = useRef(null);
+ *  const nodeRef = state ? goodbyeRef : helloRef;
  *  return (
  *    <SwitchTransition>
  *      <CSSTransition
  *        key={state ? "Goodbye, world!" : "Hello, world!"}
+ *        nodeRef={nodeRef}
  *        addEndListener={(node, done) => node.addEventListener("transitionend", done, false)}
  *        classNames='fade'
  *      >
- *        <button onClick={() => setState(state => !state)}>
+ *        <button ref={nodeRef} onClick={() => setState(state => !state)}>
  *          {state ? "Goodbye, world!" : "Hello, world!"}
  *        </button>
  *      </CSSTransition>
diff --git a/esm/Transition.js b/esm/Transition.js
index 986d29f43ade851a1b4701a6eb857339c8cb33eb..cfd1dc75cda7b0b298d7981388af6e2e189374be 100644
--- a/esm/Transition.js
+++ b/esm/Transition.js
@@ -2,7 +2,7 @@ import _objectWithoutPropertiesLoose from "@babel/runtime/helpers/esm/objectWith
 import _inheritsLoose from "@babel/runtime/helpers/esm/inheritsLoose";
 import PropTypes from 'prop-types';
 import React from 'react';
-import ReactDOM from 'react-dom';
+import ReactDOM, { flushSync } from 'react-dom';
 import config from './config';
 import { timeoutsShape } from './utils/PropTypes';
 import TransitionGroupContext from './TransitionGroupContext';
@@ -36,6 +36,7 @@ export var EXITING = 'exiting';
  *
  * ```jsx
  * import { Transition } from 'react-transition-group';
+ * import { useRef } from 'react';
  *
  * const duration = 300;
  *
@@ -51,18 +52,21 @@ export var EXITING = 'exiting';
  *   exited:  { opacity: 0 },
  * };
  *
- * const Fade = ({ in: inProp }) => (
- *   <Transition in={inProp} timeout={duration}>
- *     {state => (
- *       <div style={{
- *         ...defaultStyle,
- *         ...transitionStyles[state]
- *       }}>
- *         I'm a fade Transition!
- *       </div>
- *     )}
- *   </Transition>
- * );
+ * function Fade({ in: inProp }) {
+ *   const nodeRef = useRef(null);
+ *   return (
+ *     <Transition nodeRef={nodeRef} in={inProp} timeout={duration}>
+ *       {state => (
+ *         <div ref={nodeRef} style={{
+ *           ...defaultStyle,
+ *           ...transitionStyles[state]
+ *         }}>
+ *           I'm a fade Transition!
+ *         </div>
+ *       )}
+ *     </Transition>
+ *   );
+ * }
  * ```
  *
  * There are 4 main states a Transition can be in:
@@ -79,11 +83,15 @@ export var EXITING = 'exiting';
  * [useState](https://reactjs.org/docs/hooks-reference.html#usestate) hook):
  *
  * ```jsx
+ * import { Transition } from 'react-transition-group';
+ * import { useState, useRef } from 'react';
+ *
  * function App() {
  *   const [inProp, setInProp] = useState(false);
+ *   const nodeRef = useRef(null);
  *   return (
  *     <div>
- *       <Transition in={inProp} timeout={500}>
+ *       <Transition nodeRef={nodeRef} in={inProp} timeout={500}>
  *         {state => (
  *           // ...
  *         )}
@@ -321,25 +329,48 @@ var Transition = /*#__PURE__*/function (_React$Component) {
       this.nextCallback.cancel();
       this.nextCallback = null;
     }
-  };
+  } // safeSetState(nextState, callback) {
+  //   // This shouldn't be necessary, but there are weird race conditions with
+  //   // setState callbacks and unmounting in testing, so always make sure that
+  //   // we can cancel any pending setState callbacks after we unmount.
+  //   callback = this.setNextCallback(callback);
+  //   this.setState(nextState, callback);
+  // }
+
+  /**
+   * Prevent React 18 from batching updates.
+   * https://github.com/reactjs/react-transition-group/issues/816#issuecomment-1175783666
+   *
+   * @param {object} nextState Next state object.
+   * @param {function} callback Post-set callback.
+   */
+  ;
 
   _proto.safeSetState = function safeSetState(nextState, callback) {
-    // This shouldn't be necessary, but there are weird race conditions with
-    // setState callbacks and unmounting in testing, so always make sure that
-    // we can cancel any pending setState callbacks after we unmount.
-    callback = this.setNextCallback(callback);
-    this.setState(nextState, callback);
+    var _this4 = this;
+
+    // console.log(`Transition.js setStateConditionalFlushSync nextState=${JSON.stringify(nextState)}`)
+    if ((nextState == null ? void 0 : nextState.status) !== "exited") {
+      // console.log(`Transition.js setStateConditionalFlushSync *imperative* nextState=${JSON.stringify(nextState)}`)
+      this.setState(nextState, callback);
+      return;
+    }
+
+    flushSync(function () {
+      // console.log(`Transition.js setStateConditionalFlushSync *flushSync* nextState=${JSON.stringify(nextState)}`)
+      _this4.setState(nextState, callback);
+    });
   };
 
   _proto.setNextCallback = function setNextCallback(callback) {
-    var _this4 = this;
+    var _this5 = this;
 
     var active = true;
 
     this.nextCallback = function (event) {
       if (active) {
         active = false;
-        _this4.nextCallback = null;
+        _this5.nextCallback = null;
         callback(event);
       }
     };
@@ -415,9 +446,12 @@ var Transition = /*#__PURE__*/function (_React$Component) {
 Transition.contextType = TransitionGroupContext;
 Transition.propTypes = process.env.NODE_ENV !== "production" ? {
   /**
-   * A React reference to DOM element that need to transition:
+   * A React reference to the DOM element that needs to transition:
    * https://stackoverflow.com/a/51127130/4671932
    *
+   *   - This prop is optional, but recommended in order to avoid defaulting to
+   *      [`ReactDOM.findDOMNode`](https://reactjs.org/docs/react-dom.html#finddomnode),
+   *      which is deprecated in `StrictMode`
    *   - When `nodeRef` prop is used, `node` is not passed to callback functions
    *      (e.g. `onEnter`) because user already has direct access to the node.
    *   - When changing `key` prop of `Transition` in a `TransitionGroup` a new
@@ -439,9 +473,9 @@ Transition.propTypes = process.env.NODE_ENV !== "production" ? {
    * specific props to a component.
    *
    * ```jsx
-   * <Transition in={this.state.in} timeout={150}>
+   * <Transition nodeRef={nodeRef} in={this.state.in} timeout={150}>
    *   {state => (
-   *     <MyComponent className={`fade fade-${state}`} />
+   *     <MyComponent ref={nodeRef} className={`fade fade-${state}`} />
    *   )}
    * </Transition>
    * ```
@@ -532,7 +566,7 @@ Transition.propTypes = process.env.NODE_ENV !== "production" ? {
    * DOM node and a `done` callback. Allows for more fine grained transition end
    * logic. Timeouts are still used as a fallback if provided.
    *
-   * **Note**: when `nodeRef` prop is passed, `node` is not passed.
+   * **Note**: when `nodeRef` prop is passed, `node` is not passed, so `done` is being passed as the first argument.
    *
    * ```jsx
    * addEndListener={(node, done) => {
@@ -547,7 +581,7 @@ Transition.propTypes = process.env.NODE_ENV !== "production" ? {
    * Callback fired before the "entering" status is applied. An extra parameter
    * `isAppearing` is supplied to indicate if the enter stage is occurring on the initial mount
    *
-   * **Note**: when `nodeRef` prop is passed, `node` is not passed.
+   * **Note**: when `nodeRef` prop is passed, `node` is not passed, so `isAppearing` is being passed as the first argument.
    *
    * @type Function(node: HtmlElement, isAppearing: bool) -> void
    */
@@ -557,7 +591,7 @@ Transition.propTypes = process.env.NODE_ENV !== "production" ? {
    * Callback fired after the "entering" status is applied. An extra parameter
    * `isAppearing` is supplied to indicate if the enter stage is occurring on the initial mount
    *
-   * **Note**: when `nodeRef` prop is passed, `node` is not passed.
+   * **Note**: when `nodeRef` prop is passed, `node` is not passed, so `isAppearing` is being passed as the first argument.
    *
    * @type Function(node: HtmlElement, isAppearing: bool)
    */
@@ -567,7 +601,7 @@ Transition.propTypes = process.env.NODE_ENV !== "production" ? {
    * Callback fired after the "entered" status is applied. An extra parameter
    * `isAppearing` is supplied to indicate if the enter stage is occurring on the initial mount
    *
-   * **Note**: when `nodeRef` prop is passed, `node` is not passed.
+   * **Note**: when `nodeRef` prop is passed, `node` is not passed, so `isAppearing` is being passed as the first argument.
    *
    * @type Function(node: HtmlElement, isAppearing: bool) -> void
    */
Buy Me A Coffee