use-cycle.mjs 1.5 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647
  1. import { useRef, useState, useCallback } from 'react';
  2. import { wrap } from './wrap.mjs';
  3. /**
  4. * Cycles through a series of visual properties. Can be used to toggle between or cycle through animations. It works similar to `useState` in React. It is provided an initial array of possible states, and returns an array of two arguments.
  5. *
  6. * An index value can be passed to the returned `cycle` function to cycle to a specific index.
  7. *
  8. * ```jsx
  9. * import * as React from "react"
  10. * import { motion, useCycle } from "framer-motion"
  11. *
  12. * export const MyComponent = () => {
  13. * const [x, cycleX] = useCycle(0, 50, 100)
  14. *
  15. * return (
  16. * <motion.div
  17. * animate={{ x: x }}
  18. * onTap={() => cycleX()}
  19. * />
  20. * )
  21. * }
  22. * ```
  23. *
  24. * @param items - items to cycle through
  25. * @returns [currentState, cycleState]
  26. *
  27. * @public
  28. */
  29. function useCycle(...items) {
  30. const index = useRef(0);
  31. const [item, setItem] = useState(items[index.current]);
  32. const runCycle = useCallback((next) => {
  33. index.current =
  34. typeof next !== "number"
  35. ? wrap(0, items.length, index.current + 1)
  36. : next;
  37. setItem(items[index.current]);
  38. },
  39. // The array will change on each call, but by putting items.length at
  40. // the front of this array, we guarantee the dependency comparison will match up
  41. // eslint-disable-next-line react-hooks/exhaustive-deps
  42. [items.length, ...items]);
  43. return [item, runCycle];
  44. }
  45. export { useCycle };