在useEffect挂钩中运行动画是否安全?

时间:2019-09-06 11:00:41

标签: reactjs d3.js react-hooks

我已经开始在代码中引入一些React钩子,特别是useEffect,我似乎无法找出我在做什么是否安全。本质上,我在挂钩内的DOM上运行动画,并且我想确保不会破坏任何DOM快照。

这是一个示例,我从完整的示例中进行了修改,以尝试简洁明了地说明正在发生的事情:

export function GrowingCircle(props) {

  const root = useRef(null);          // This is the root element we draw to

  // The actual rendering is done whenever the data changes
  useEffect(() => {

    const radius = props.width / 2;

    d3.select(root.current)
      .transition()
      .duration(1000)
      .attr("r", radius);

  }, [props.width]);

  return (
      <svg width={props.width} height="100%">
        <circle ref={root} cx="0" cy="0" r="0" fill="red" />
      </svg>
    );
}

我关心的部分是.transition()将在DOM上频繁运行更新1秒钟,我不确定这是否会影响react渲染?


一个后续问题(通常无法像本例一样控制动画渲染)。 circle不在JSX内的以下内容是否会更改?

export function GrowingCircle(props) {

  const root = useRef(null);          // This is the root element we draw to

  // The actual rendering is done whenever the data changes
  useEffect(() => {

    const radius = props.width / 2;

    d3.select(root.current)
      .append("circle")
      .attr("cx", 0)
      .attr("cy", 0)
      .attr("fill", "red")
      .transition()
      .duration(1000)
      .attr("r", radius);

  }, [props.width]);

  return (
      <svg ref={root} width={props.width} height="100%">
      </svg>
    );
}

2 个答案:

答案 0 :(得分:1)

React渲染的

DOM元素可以被修改。 但是如果由于虚拟DOM与当前DOM(React具有的当前DOM)不匹配而使React需要重新渲染,则它可能会替换已修改的部分,以及对React所做的更改可能会丢失。

React修改了必要的部分,因此,如果React仅修改了元素的一部分或将其删除,则修改可能会保留。因此,据我了解,我们不能相信修改将继续存在。仅当我们确定在自定义修改后Component输出不会改变时。

我的建议是使用React来跟踪任何更改:

  • 使用useStateuseEffect来修改style属性。
  • 使用CSS类并使用CSS处理动画。
  • 使用为React创建的动画库(请看Framer motion)。

答案 1 :(得分:0)

我一直在您的相同场景中,给您一个简短的答案,因为没有真正的D3-React实现,因此您需要绘制命令式与声明式渲染的边界。但是,在两个示例中,您实际上都没有违反任何规则。

在第二个示例中,您将the绳传递给D3进行完整渲染,而React simple保留对顶级svg元素的引用。在第一个示例中,您将渲染单个圆,并且仅使用D3管理其过渡。

但是,在第一个示例中,由于看起来您声明后从未更改DOM中的任何内容,因此React应该通常不会进行干预。这是第三个示例,实现了与您编写的内容类似的内容:

export function GrowingCircle(props) {

  const root = useRef(null);          // This is the root element we draw to

  const [radius,setRadius] = useState(0);  //initial radius value

  useEffect(() => {

    d3.select(root.current)
      .transition()
      .duration(1000)
      .attr("r", props.width / 2)
      .on("end", () => {setRadius(props.width / 2)});   //this is necessary

  }, [props.width]);

  return (
      <svg width={props.width} height="100%">
        <circle ref={root} cx="0" cy="0" r={radius} fill="red" />
      </svg>
    );
}

我本人也遇到了类似的问题,据我了解,在过渡期间d3直接作用于您想要的值,导致它不触发react的重新渲染机制。但是,一旦完成,我假设它直接尝试对半径值起作用,并且您的圆会捕捉回到其原始值0。因此,您只需添加.on('end')事件以更新其状态,就可以了!

我觉得这与react方法差不多,其中d3仅根据react状态选择渲染的元素。