preact-router icon indicating copy to clipboard operation
preact-router copied to clipboard

preact-router and preact-transition-group

Open mattdesl opened this issue 8 years ago • 12 comments

Hi, first of all thanks for Preact and its tooling, it's really great. 👍

I'm looking to add preact-transition-group to my routing so that each component can handle componentWillEnter, componentWillAppear and componentWillLeave.

So far I haven't found a clean way of integrating these two together without patching this module.

https://github.com/Jam3/preact-router/commit/966d4f227c5ae3ab23bf311f2dab86f0dd9761fb#diff-1fdf421c05c1140f6d71444ea2b27638L248

Is there another way this might be possible?

mattdesl avatar May 05 '17 18:05 mattdesl

Maybe something like this would work, as a wrapper around router?

import Router from 'preact-router';
import TransitionGroup from 'preact-transition-group';

const TransitionRouter = ({ children, ...props }) => {
  <Router {...props}>
    { children.map( child =>
      <TransitionGroup {...child.attributes}>
        {child}
      </TransitionGroup>
    ) }
  </Router>
}

If not, I wondered about just doing it inline:

<Router>
  <TransitionGroup path="/" default>
    <SomeRoute a="b" />
  </TransitionGroup>
  <TransitionGroup path="/one">
    <OtherRoute />
  </TransitionGroup>
  <TransitionGroup path="/two">
    <AnotherRoute />
  </TransitionGroup>
</Router>

developit avatar May 06 '17 19:05 developit

Thanks for the reply! I tried both but unfortunately neither give me acceptable results.

In your examples you have one transition group per component, but really we should just have one TransitionGroup for all routes/components — that way it can keep track of which components are appearing/leaving in that group.

Also, it seems like the Router only looks at top-level children, so first example is trying to match against TransitionGroups instead of the routes.

So it should ultimately look like this, somehow:

<Router><TransitionGroup>{childrenToMatch}</TransitionGroup></Router>

mattdesl avatar May 09 '17 14:05 mattdesl

Correct, it only supports one level of children. I think the only way to make this work would be to use a function-as-children approach, which is not currently supported in preact-router, but seems like it could be. Unfortunately the nesting approach you suggested isn't possible because it assumes the component tree to be pre-resolved, whereas children may be derived from some inner render.

<Router>
  { ({ match }) =>
    <TransitionGroup>
      { match([
        <SomeRoute path="/" default a="b" />,
        <OtherRoute path="/one" />,
        <AnotherRoute path="/two" />
      ]) }
    </TransitionGroup>
  }
</Router>

Not sure on the API though. This is a tricky one. Really it'd be nice if Router could pierce the hierarchy, but that'd be quite a bit more complexity. It's something I'd love to do, but it would be a rewrite.

developit avatar May 09 '17 14:05 developit

Thanks, for now I am happy to use my fork. We eventually might clean it up a bit and re-release it with more documentation on transitions, as an alternative to preact-router for more specialized animated apps.

Cheers. :smile:

mattdesl avatar May 11 '17 19:05 mattdesl

sounds awesome, looking forward to it!

developit avatar May 17 '17 06:05 developit

Also looking for a solution for CSS transitioning between <Route/>s. Tried this https://github.com/developit/preact-router/issues/187#issuecomment-299662622, but does not work as expected.

t47io avatar May 29 '17 06:05 t47io

One option here might be to allow passing a "wrap" function to the router. The router would pass the active child through that function, so you could consistently wrap each route in a TransitionGroup:

<Router wrap={ c => <CSSTransitionGroup>{c}</CSSTransitionGroup> }>
  <A path="/" />
  <B path="/b" />
  <C path="/c" />
</Router>

I'm curious why the direct wrapping doesn't work though. Maybe it's because CSSTransitionGroup doesn't passthrough props to its child? Could fix that like so:

import { h, cloneElement } from 'preact';
const TransitionGroup = ({ children, transitionEnter, transitionLeave, component, ...props }) => (
  <CSSTransitionGroup {...{ transitionEnter, transitionLeave, component }}>
    {cloneElement(children[0], props)}
  </CSSTransitionGroup>
);

<Router>
  <TransitionGroup path="/" transitionEnter="enter" transitionLeave="leave">
    <A />
  </TransitionGroup>
  <TransitionGroup path="/b" transitionEnter="enter" transitionLeave="leave">
    <B />
  </TransitionGroup>
</Router>

developit avatar Jun 03 '17 19:06 developit

@mattdesl would extending the router work for you?

import { h } from 'preact';
import Router from 'preact-router';
import TransitionGroup from 'preact-transition-group';

export default class TransitionRouter extends Router {
    render(props, state) {
        return (
            <TransitionGroup component="div">
                {super.render(props, state)}
            </TransitionGroup>
        );
    }
}

danalloway avatar Sep 21 '17 15:09 danalloway

also, there is this -> https://github.com/prateekbh/liquid-route

danalloway avatar Sep 21 '17 15:09 danalloway

@danalloway 's tip works for me, This fiddle shows how to make it work. However, the transition will be broken if I use prateekbh/preact-async-route to wrap route components (both sync and async use).

fantasia949 avatar Oct 08 '17 04:10 fantasia949

Hi ! The tips from @danalloway is not working for me, is this way of using preact router and preact transition group is still relevant ? @fantasia949 i also tried the code from your fiddle, the components seems to not mount with the last version of preact. i'm using :

"preact": "^10.3.2", "preact-router": "^3.2.1", "preact-transition-group": "^1.1.1",

glegenty avatar May 02 '20 01:05 glegenty

Finally i managed to make it work with the last version of react-transition-group. I had to switch to react-snap for prerendering, the prerender from preact-cli seems to not work with react-transition-group.

glegenty avatar May 03 '20 08:05 glegenty