这取决于你想如何与你的组件交互,你想使用 api 还是仅仅提供更新的 props?
// update by regenerating springProps from some other state
const springProps = useSpring(props)
// update springProps with api
const [springProps, api] = useSpring(() => props)
通过再生弹簧进行更新
在第一个示例中,使用其中一个动画原生元素 (animated.XXX),您将在每次想要更新状态时重新渲染动画包装,因为您每次都会传递一个新的、更新的 springProps 对象您更新它,这将导致它重新呈现。或者...这只是部分正确,返回对象中的单个 SpringValue 将与两种方法相同,因此如果您将它们作为单独的道具传递,理论上不需要重新渲染,但如果您将它们作为整个对象(例如,当 springProps 是 style 对象时)它将是上次渲染后更改的对象,并导致 animated 包装器的重新渲染。
当您使用在包装器内不使用 ref 的自定义元素而不是原生元素之一时,组件将为每个动画帧重新渲染一次。从性能的角度来看,这是次优的,但是通过这种策略,您可以为组件使用您想要的任何 api(prop 名称),就像您所做的那样。
这是一个使用此解决方案的沙盒:sandbox
您可以观察到动画可以正常工作,但也可以看到组件重新渲染了很多。
通过 api 更新
如果您想提高效率,并使用 api 更新您的自定义组件,您需要遵守一些规则。当您使用 api 时,react-spring 通过相应 DOM 元素上的 ref 更新元素,因此,react-spring 必须了解如何更新元素而不需要 React 重新渲染它。这意味着:
- 您的自定义元素必须能够保存引用
- 您的自定义元素必须将 ref 附加到您希望在其上发生所有动画的 DOM 元素。因为
forwardRef 不允许您添加多个引用,所以如果您想将自定义组件内的多个 DOM 元素包装在 animated 中,则无法对它进行更改(还有其他策略可以解决此问题,例如不包装您的animated 中的组件,而是使用组件内的原生 animated.XXX 元素并将 SpringValues 作为道具传递)。
- 您的自定义组件的属性名称必须与您要更新的 ref 附加到的 DOM 元素上的属性相对应。否则
react-spring 将无法理解如何更新此元素。
由于您的元素有很多自定义属性,react-spring 将无法通过 api 更新您的组件。它将尝试通过简单地在 ref 附加到的元素上设置更新的属性来做到这一点,但是由于您需要 React 将自定义属性映射到 DOM 元素上的实际属性,这将失败(我没有知道有你提供的一组属性)。
作为如何完成此操作的示例,这里有一个显示此策略大纲的沙盒:sandbox
在这里你可以看到组件只渲染了一次并且仍然可以更新。您还可以看到,包裹在animated 中的自定义组件上使用的属性对应于div 元素上的props,从而使react-spring 能够完全按照我们的意愿行事(AnimatedHomeContent 的子元素是React 命名为 props.children,react-spring 知道如何处理)。
<AnimatedHomeContent style={{ backgroundColor: spring.backgroundColor }}>
{spring.content}
</AnimatedHomeContent>
在沙盒中也是一个使用其他属性名称的坏例子。
<AnimatedHomeContentBad
backgroundColor={spring.backgroundColor}
content={spring.content}
/>
为了从这个版本中获得正确的行为,React 必须处理组件以将输入道具映射到本机 DOM 元素道具。通过 api 更新此版本时,react-spring 不会重新渲染组件(因为它可以获取 ref),而是在 ref 所附加的元素上设置更新的属性(检查控制台中的元素以亲自查看这些道具是在更新后设置的)。由于属性不是真正的属性,因此组件上没有任何更新或动画。