react-spring @ next - пружина с асинхронной функцией as 'to', анимирующая каждый рендер

Пружина react-spring (версия 9), значение «to» которой является асинхронной функцией, проходит цикл анимации при каждом повторном рендеринге. Если «to» - это простой объект, анимация запускается только изначально, как и ожидалось.

Рассмотрим этот компонент:

const Component = () => {
    // let's trigger some rerenders
    const [state, setState] = useState(false);
    useEffect(() => {
        setInterval(() => {
            setState(x => !x);
        }, 1000);
    }, []);

    // a spring with an async func provided to 'to'
    const props = useSpring({
        to: async (next, cancel) => {
            await next({opacity: 1, color: '#ffaaee'})
            await next({opacity: 0, color: 'rgb(14,26,19)'})
        },
        from: {opacity: 0, color: 'red'}
    });

    return <animated.div style={props}>I will fade in and out</animated.div>
};

Текст будет мигать вечно.

Я считаю, что это неправильное поведение. Это баг, или я что-то не так делаю?


person Vlop    schedule 31.01.2020    source источник


Ответы (1)


Я думаю, что предполагаемое поведение - показать текущее состояние свойства to для useSpring. Когда он постоянный, он всегда будет показывать одно и то же состояние при каждом рендеринге. Но вы можете изменить свойство usepring на. Например:

const ComponentColor = () => {
  const [color, setColor] = React.useState("red");

  const props = useSpring({
    to: { color },
    from: { color: "red" }
  });

  return (
    <>
      <animated.h2 style={props}>This color will change</animated.h2>
      <button onClick={() => setColor("blue")}>blue</button>
      <button onClick={() => setColor("green")}>green</button>
      <button onClick={() => setColor("orange")}>orange</button>
    </>
  );
};

В этом случае цвет текста изменится на цвет, который вы нажали. Думаю, ваш пример соответствует этому. При каждом рендеринге он будет показывать текущее состояние свойства to, которое является последовательностью. Так что я думаю, что это предполагаемое поведение.

Если вы хотите, чтобы этот useState был анимирован только при первом рендеринге. Затем вы можете выполнить рефакторинг анимационной части для нового компонента и убедиться, что он будет отображаться только в первый раз. Например, если вы используете React.memo, он будет повторно отображать ваш функциональный компонент, только если одно из его свойств изменится. В этом примере нет свойства, поэтому он будет отображаться только в первый раз.

const Component = () => {
  // let's trigger some rerenders
  const [state, setState] = React.useState(false);
  React.useEffect(() => {
    setInterval(() => {
      setState(x => !x);
    }, 2000);
  }, []);

  return <FirstTimeAnimate />;
};

const FirstTimeAnimate = React.memo(() => {
  const props = useSpring({
    to: async (next, cancel) => {
      await next({ opacity: 0.25, color: "#black" });
      await next({ opacity: 1, color: "blue" });
    },
    from: { opacity: 0, color: "red" }
  });

  return <animated.h2 style={props}>I will fade in and out</animated.h2>;
});

https://codesandbox.io/s/fervent-margulis-drt5l

person Peter Ambruzs    schedule 31.01.2020