Ilja
Ilja

Reputation: 46479

useTransition mounts new object instantly

I am trying to figure out how to utilise useTransition for page transitions (simple opacity change where first page fades out and new one fades in).

So far I have this small demo going https://codesandbox.io/s/sleepy-knuth-xe8e0?file=/src/App.js it somewhat works, but weirdly. When transition starts new page is mounted instantly while old one starts animating. This causes various layout issues and is not behaviour I am after. Is it possible to have first element fade out and only then mount and fade in second element?

Code associated to demo

import React, { useState } from "react";
import "./styles.css";
import { useTransition, a } from "react-spring";

export default function App() {
  const [initial, setInitial] = useState(true);
  const transition = useTransition(initial, {
    from: { opacity: 0 },
    enter: { opacity: 1 },
    leave: { opacity: 0 }
  });

  return (
    <div>
      {transition((style, initial) => {
        return initial ? (
          <a.h1 style={style}>Hello Initial</a.h1>
        ) : (
          <a.h1 style={style}>Hello Secondary</a.h1>
        );
      })}
      <button onClick={() => setInitial(prev => !prev)}>Change Page</button>
    </div>
  );
}

Upvotes: 0

Views: 1303

Answers (2)

hackhan
hackhan

Reputation: 522

you can delay the start of the transition by waiting for the leave animation to complete.

const sleep = t => new Promise(res => setTimeout(res, t));

...

const transition = useTransition(initial, {
  from: { position: "absolute", opacity: 0 },
  enter: i => async next => {
    await sleep(1000);
    await next({ opacity: 1 });
  },
  leave: { opacity: 0 }
});

This delays the animation also for the very first time it is run. You can have a ref to keep track of whether the component has been rendered before or if it is its first time rendering, then you can skip sleep call if it's the first render.

OR

You can just simply provide trail config

const transition = useTransition(initial, {
    from: { position: "absolute", opacity: 0 },
    enter: { opacity: 1 },
    leave: { opacity: 0 },
    trail: 300
  });

Upvotes: 3

Antoni
Antoni

Reputation: 1433

You need to add position: absolute and then you need to set the right position with css.

import React, { useState } from "react";
import "./styles.css";
import { useTransition, a } from "react-spring";

export default function App() {
  const [initial, setInitial] = useState(true);
  const transition = useTransition(initial, {
    from: { position: 'absolute', opacity: 0 },
    enter: { opacity: 1 },
    leave: { opacity: 0 }
  });

  return (
    <div>
      {transition((style, initial) => {
        return initial ? (
          <a.h1 style={style}>Hello Initial</a.h1>
        ) : (
          <a.h1 style={style}>Hello Secondary</a.h1>
        );
      })}
      <button onClick={() => setInitial(prev => !prev)}>Change Page</button>
    </div>
  );
}

Upvotes: 0

Related Questions