资讯动态

react-native-reanimated 1.x `onChange` 节点详解:监听值变化并触发响应逻辑

发布时间:2026/9/15 11:41:02 来源:尧图企业网站定制
react-native-reanimated 1.xonChange节点详解监听值变化并触发响应逻辑【免费下载链接】react-native-reanimatedReact Natives Animated library reimplemented项目地址: https://gitcode.com/GitHub_Trending/re/react-native-reanimatedonChange是 react-native-reanimated 1.x 声明式动画节点系统中的一个基础节点它的职责非常聚焦在动画节点图中比较某个值与其上一次求值结果一旦发现值发生变化就执行指定的action节点并返回其结果。本文将以仓库中的原始文档 onChange.md 为骨架结合同版本节点文档cond、set、diff、block、call等与 声明式动画 API 说明讲清楚它的用法、适用场景、求值时机与易踩的坑读完即可在 v1 风格的节点图中熟练使用onChange。一、onChange的签名与核心语义onChange节点的 API 形式非常简洁onChange(value, action);官方文档 onChange.md 对其语义的定义如下When evaluated, it will comparevalueto its previous value. If it has changed,actionwill be evaluated and its value will be returned.翻译成通俗的表述onChange在每次被求值时都会执行以下三个步骤计算当前值对第一个参数value节点进行一次求值与上一次的值比较把当前求值结果与上一次求值时保存的旧值做比较决定是否执行action如果两者不相等即值发生了“变化”则对第二个参数action节点进行求值并把action的求值结果作为整个onChange节点的返回值如果值没有变化则不会求值action。参数说明参数类型说明value节点node被监视的节点可以是Value、clock的position、运算节点如add、multiply等任何能求值的节点action节点node当value发生变化时被求值的节点通常是block组合多个副作用节点或带副作用的节点例如set、startClock、call等二、onChange在节点系统中的位置与定位要真正理解onChange需要先理解 react-native-reanimated 1.x 与原生Animated的根本差异。正如 about.md 与 declarative.md 所强调的在 v1 中动画不再是“动画对象”而是由一个个可以被求值的节点组成的图。开发者通过block、cond、set、startClock、timing等节点手工编排“每一帧要做什么”。在这种执行模型下节点分为两大类纯计算节点如add、multiply、sub、diff、interpolate它们根据输入节点求值并返回一个数值不产生副作用带副作用节点如set赋值、startClock/stopClock启停时钟、call回调 JS、debug打印日志。onChange属于典型的条件副作用调度节点它本身不产生动画数值而是充当“监听器”把一个“值是否变化”的事实翻译成“是否执行action”的控制流。这与cond条件分支、set赋值、call回调 JS等节点形成了天然的搭配关系相关节点文档见 nodes/cond.md、nodes/set.md、nodes/block.md、nodes/call.md。与cond的分工差异cond是“基于当前值做分支”每次求值都读取conditionNode为真走ifNode否则走elseNode且每次求值都会执行分支。onChange则是“基于值的变化做触发”只有当前值与上次不同才执行action。这带来一个关键区别——onChange天然具有“事件”语义它适合表达“当 X 变了做一次 Y”而不是“根据 X 的值一直做 Y”。与diff的对比diff节点见 nodes/diff.md会返回当前值与上一次求值值的差值首次求值返回节点自身值。它关心的是“变化了多少”输出的是一个连续变化的数值常用于速度、增量等计算而onChange关心的是“是否变化”输出的是action节点的执行结果。二者一个是量的度量一个是事件的触发可根据需求选用。三、典型使用场景onChange最常见的用途是把“值变化”这个条件转换为一次性的副作用执行典型场景包括1. 动画状态机切换当某个值如手势位移、进度越过某个临界点时希望启动另一段动画。例如在 declarative.md 展示的runTiming风格代码里配合cond与set可以写成import Animated, { Easing } from react-native-reanimated; const { Clock, Value, block, cond, onChange, set, startClock, stopClock, timing, clockRunning } Animated; function runTiming(clock, value, dest) { const state { finished: new Value(0), position: new Value(0), time: new Value(0), frameTime: new Value(0), }; const config { duration: 5000, toValue: new Value(0), easing: Easing.inOut(Easing.ease), }; return block([ cond( clockRunning(clock), [set(config.toValue, dest)], [ set(state.finished, 0), set(state.time, 0), set(state.position, value), set(state.frameTime, 0), set(config.toValue, dest), startClock(clock), ] ), timing(clock, state, config), cond(state.finished, stopClock(clock)), // 监听 position 的变化一旦位置开始移动就同步其他节点 onChange(state.position, set(this._shadowX, state.position)), state.position, ]); }这里onChange(state.position, ...)的作用是只要position在动画推进过程中发生变化就同步派生出一个“影子位置”节点用于跟手阴影、拖尾等视觉效果。2. 把“值变化”转换为 JS 回调onChange常与call节点配合把 UI 线程上的值变化“通知”回 JS 侧。call的语义是当argsNodes中的任意节点更新时以这些节点的当前值为参数调用 JS 回调见 nodes/call.md。两者组合可以实现“某个动画值每次变化时通知 JS 做一次记录或上报”import Animated from react-native-reanimated; const { Value, block, onChange, call } Animated; const progress new Value(0); const listenerNode block([ onChange( progress, call([progress], (args) { console.log(progress changed to, args[0]); }) ), ]); // 将 listenerNode 挂载到 Animated.Code 或 useCode 中持续求值不过需要说明由于call本身就具备“值更新即回调”的能力单纯为了通知 JS 而包一层onChange时可以评估是否直接用call更简洁onChange的价值更多体现在在 UI 线程的节点图内部按变化触发其他节点运算而非跨线程通知。3. 一次性的初始化/重置逻辑因为onChange只有在值发生变化时才执行action它可以用来表达“值第一次被赋值时做一次初始化”这类一次性逻辑避免在每一帧重复执行有副作用的节点——这正符合 v1 “副作用节点不应被频繁执行”的设计原则。四、求值时机理解“一帧最多求值一次”onChange是否可靠地触发取决于它是否被持续求值。react-native-reanimated 1.x 采用“每帧每个节点最多求值一次at most once evaluation”的算法见 about.md每帧先分析事件如触摸流事件可能更新某些Animated.Value更新所有“运行中”的clock节点对应的值从本周期被更新的节点出发遍历节点树寻找与视图相连的最终节点对找到的节点求值这会递归触发其输入节点的求值若仍有运行中的时钟则排队进入下一帧重复上述过程。这意味着只有被视图属性引用如transform、opacity或被Animated.Code/useCode挂载的节点才会被纳入求值遍历onChange必须处在“可达”的求值路径上其“值变化检测”才有意义当节点值被缓存后同帧内再次引用返回缓存值onChange不会重复执行action这保证了副作用不会在一帧内被多次触发。所以一个常见实践是把包含onChange的节点放在Animated.Code组件或Animated.useCode中挂载执行。按 code.md 的说明Animated.Code渲染null可在 render 方法任意位置放置并在componentDidMount时安装节点useCode(factory, deps)则在组件挂载以及依赖数组变化时更新节点import Animated from react-native-reanimated; const { Value, block, onChange, set } Animated; function Example() { const progress new Value(0); const lastSnap new Value(0); Animated.useCode( () block([ onChange(progress, set(lastSnap, progress)), ]), [] // 节点只挂载一次若依赖 progress 的重建可把相关依赖放入数组 ); return null; }五、使用注意事项与边界行为基于文档语义与节点系统特性使用onChange时有几点需要注意action只在“值变化”时求值如果value节点在连续多帧内保持相同求值结果action不会被执行这是与cond最大的行为差异设计逻辑时不要假设action每帧都运行。首次求值的比较基准onChange需要与“上一次”的值比较。在其首次求值时上一次的基准值尚未建立此时的比较行为取决于具体实现通常首次求值即建立基准或直接判定为变化。在编写依赖“仅首次不触发”的逻辑时应谨慎建议用其他状态节点如condclockRunning显式控制初始化流程。action返回值即onChange返回值当值未变化时onChange没有可返回的action结果因此它不适合作为纯数值计算的来源更不应该把它当作set、add等计算节点的“数值输入”。它应当被用作节点块中的“副作用声明”而不是数值管道。与block组合使用onChange的action通常需要同时做多件事更新多个节点此时应使用block([...])包裹因为block会按数组顺序求值所有节点并返回最后一个节点的值见 nodes/block.md。避免高频副作用由于onChange会在值变化的每一帧触发action如果action中包含call这类跨线程回调变化频率过高时会产生大量 JS 回调需要结合cond、diff或阈值比较等手段做节流。六、结语onChange是 react-native-reanimated 1.x 声明式节点体系中“事件化触发”的关键拼图它用最小的 API两个参数把“值是否变化”的检测与“执行动作”的副作用串联起来与cond、set、call、block等节点共同构成了 v1 在 UI 线程上编排复杂交互的基础能力。理解它的求值时机依赖“每帧最多求值一次”的算法与边界行为仅在变化时触发、返回值不适合做数值管道就能在状态机切换、动画同步、一次性初始化等场景中写出既高效又符合声明式范式的节点代码。如需查看更多相关节点可继续阅读仓库中的 nodes/cond.md、nodes/set.md、nodes/diff.md、nodes/call.md 与 nodes/block.md以及总览性的 declarative.md。【免费下载链接】react-native-reanimatedReact Natives Animated library reimplemented项目地址: https://gitcode.com/GitHub_Trending/re/react-native-reanimated创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价