1
votes

I work with the lottie-web package in a .web.js file under React Native (Expo).

My problem is that the let variable anim is undefined in the functions in useImperativeHandle. In the useEffect anim works wonderfully. For example if I access anim.play() right after the initialize at useEffect it works. But at the play() function in imperative it doesn't work.

At my parent component I create a ref with useRef and pass the ref to the component

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);
  },
}));
1

1 Answers

1
votes

That because React has no clue what anim is when it creates API functions in useImperativeHandle (duo to closure and react updating strategies which don't trigger any update by mutating a variable). There is some way you can handle this, after all, it comes to personal opinion what to do, I will use something like this which work the best for me.

Add GetApi Function

// 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
});

store anim in a state

store anim in the state for first render only and use it in dependencies array of getApi useEffect

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

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

React.useImperativeHandle(ref, getAPI(anim), [anim]);