Search code examples
javascriptreactjsreact-routerreduxreact-router-redux

Getting ReactTransitionGroup and Redux-Simple-Router to work together


My coworker and I are trying to wrap redux-simple-router navigation (which we've had working for a while) in a ReactTransitionGroup element so that its lifecycle methods can be called on a route change. We've tried a whole bunch of versions, but haven't figured out a way to get it working.

Our current iteration creates a wrapper right under the ReactTransitionGroup (which is a child of Provider) so that lifecycle methods can be defined at a place where both the route state and children are accessible. This wrapper's render and componentWillAppear methods are definitely called on initial app load (in the browser), but not when navigating (using Redux Simple Router) between pages in the app. That code, more or less, is on this gist.

Most notably, we tried the cloneElement method from React with a regenerated key (e.g., through history.createKey()) each time, but couldn't get the lifecycle methods to fire on any route changes.

What's the idiomatic way to wrap simple-router components in a ReactTransitionGroup with redux-simple-router?

UPDATES

I've tried a new version, in which I wrap each individual route with its own TransitionWrapper class, as in this second gist (inspired by this answer).

When that TransitionWrapper extends React.Component (as in the example), the render method is called, but nothing else. The site works fine (but I can't access the transition hooks).

When it extends ReactTransitionGroup, I hit an error on line 99 of the performAppear method on the ReactTransitionGroup object, a snippet of which is below:

performAppear: function (key) {
  this.currentlyTransitioningKeys[key] = true;

  var component = this.refs[key];

  if (component.componentWillAppear) { /* error here */
    component.componentWillAppear(this._handleDoneAppearing.bind(this, key));
  } else {
    this._handleDoneAppearing(key);
  }

Uncaught TypeError: Cannot read property 'componentWillAppear' of undefined

That's getting raised, because this.refs is an empty object. (key is correct).

Notably, if I close Chrome debugger so that the error is skipped, the site still works fine.


Solution

  • Our solution was to create a function that returned another function that in turn returned the component wrapped in a ReactTransitionGroup.

    var transitionWrap = (component, myKey) => {
      return () => {
        return ( 
          <ReactTransitionGroup>
            { React.createElement(component, { key: myKey } ) }
          </ReactTransitionGroup>
        )
      }
    }
    
    const routeConfig = [
      {
        path: "/",
        component: AppContainer,
        childRoutes: [
          { path: "/signed-out",
            component: LoggedOut,
            childRoutes: [
              { path: "/home", component: transitionWrap(Home, browserHistory.createKey())                            }
            ]
          }
        ]
      }
    ];
    
    ReactDOM.render(
      <div>
        <Provider store={store}>
          <Router history={browserHistory} routes={routeConfig} />
        </Provider>
      </div>,
      document.getElementById('app')
    );
    

    Works perfectly (giving the component access to Transition Group methods), with one exception -- the child component (ie Home) cannot be connected to Redux global state. If it needs to be, that component can essentially just be a wrapper that renders another component that connects to Redux state, so that Home (for example) is really just there to handle animations, and its child component takes care of everything data-related.