переменная let не определена в функции useImperativeHandle

Я работаю с пакетом lottie-web в файле .web.js в React Native (Expo).

Моя проблема в том, что переменная let anim не определена в функциях в useImperativeHandle. В использовании useEffect anim прекрасно работает. Например, если я обращаюсь к anim.play () сразу после инициализации в useEffect, он работает. Но с функцией play () в императиве это не работает.

В моем родительском компоненте я создаю ссылку с useRef и передаю ссылку компоненту

const lottieAnim = useRef(null);

--

let anim;
useEffect(() => {
  const lottieOptions = {
    container: divContainer.current,
    renderer: 'svg',
    loop,
    autoplay,
    segments: segments !== false,
    animationData,
    rendererSettings,
  };
  anim = lottie.loadAnimation({ ...lottieOptions, ...options });
  // anim.play(); works here 

  if (onAnimationFinish) {
    anim.addEventListener('complete', onAnimationFinish);
  }
}, []);

useImperativeHandle(ref, () => ({
  stop() {
    anim.stop();
  },
  pause() {
    anim.pause();
  },
  play() {
    anim.play();
    // anim is undefined
  },
  setSpeed(s) {
    anim.setSpeed(s);
  },
  setDirection(d) {
    anim.setDirection(d);
  },
  playSegments(s) {
    anim.playSegments(s);
  },
}));

person Arbnor    schedule 26.01.2021    source источник


Ответы (1)


Это связано с тем, что React не имеет понятия, что такое анимация, когда создает функции API в useImperativeHandle (дуэт для стратегий закрытия и реагирования, которые не запускают какое-либо обновление путем изменения переменной). Есть какой-то способ справиться с этим, в конце концов, это зависит от личного мнения, что делать, я буду использовать что-то вроде этого, что лучше всего подходит для меня.

Добавить функцию GetApi

// hanlder.js
const stop = anim => anim.stop()
// api.js
const getAPI = anim => () => ({
  stop: stop.bind(this, anim),
  // or simply
  setSpeed: s => anim.setSpeed(s),

  // you can mock APIs here if anim is null or throw an Error
});

хранить аним в состоянии

сохранить анимацию в состоянии только для первого рендеринга и использовать его в массиве зависимостей getApi useEffect

const [anim, setAnim] = React.useState(null);

React.useEffect(() => {
  // initialization part
}, []);

React.useImperativeHandle(ref, getAPI(anim), [anim]);
person Amirhossein Ebrahimi    schedule 26.01.2021
comment
Отлично, сэр. Работает, спасибо за подробное описание проблемы! - person Arbnor; 26.01.2021