React:使用自定义挂钩处理动态引用

React: Handle dynamic refs with custom hook

我正在构建一个严重依赖动画的应用程序,但在使用动态引用和自定义挂钩时遇到了一些问题。

所以,我有一个自定义挂钩,它添加了点击事件侦听器、处理动画和 returns 引用。

const usePageChange = () => {
  const node = useRef(null);
  const tl = useMemo(() => gsap.timeline({ paused: true }), []);

  useEffect(() => {
    // Set GSAP timeline
    // omited for the sake of brevety
    return () => {
      tl.kill();
    };
  }, []);

  useEffect(() => {
    const set = (ev) => {
      ev.preventDefault();
      tl.play();
    };
    
    const { current } = node;
    current.addEventListener('click', set);

    return () => {
      current.removeEventListener('click', set);
    };
  }, [tl]);

  return node;
};

这适用于非动态的 refs

const Home = () => {
  const elementRef = usePageChange();

  return (
    <div className='slides'>
      <img
        alt='some alt'
        src='/public/someimage.jpg'
        ref={elementRef}
      />
    </div>
  );
};

但我一直没能找到一种方法来使用动态 refs 而不破坏 Rules of Hooks.

这是我试过的(简化的),但显然不正确。无法在回调中调用 React Hook:useRef(projects.map(() => usePageChange()));

const Home = () => {
  const projects = useSelector((state) => state.projects.data);

  const elementsRef = useRef(projects.map(() => usePageChange()));

  return (
    <div className='slides'>
      {projects.map((project, i) => (
        <img
          alt={`${project.image_alt}`}
          src={`/public/${project.image_path}`}
          ref={elementsRef[i]}
        />
      ))}
    </div>
  );
};

我真正需要的是一种以某种方式遍历项目长度并动态调用 usePageChange 挂钩的方法。

至此,我不知道直接给每个动态元素添加点击事件,在组件内部处理动画会不会更好

对于这样的事情,赋予每个列表项状态比尝试在父组件中维护某种子状态数组更有意义。

const ProjectListItem = ({ project }) => {
  const elementRef = usePageChange();

  return (
    <img
      alt={`${project.image_alt}`}
      src={`/public/${project.image_path}`}
      ref={elementRef}
    />
  );
}

const Home = () => {
  const projects = ...;

  return (
    <div className='slides'>
      {projects.map(project => (
        <ProjectListItem project={project} />
      ))}
    </div>
  );
};

如果这对您不起作用,您可能想尝试 ref 属性的回调形式:

const usePageChange = () => {
  const [node, setNode] = useState(null);

  // ...

  return { node, setNode };
};

const ProjectListItem = ({ project }) => {
  const { setNode } = usePageChange();

  return (
    <img
      alt={`${project.image_alt}`}
      src={`/public/${project.image_path}`}
      ref={setNode}
    />
  );
}