re-frame受控输入框更新db数值时无法输入小数问题咨询
re-frame 文本输入框输入小数时小数点被吞问题修复方案
问题核心原因
受控文本输入框的渲染值必须和用户输入的原始字符串完全匹配,当前实现直接在on-change阶段把输入字符串用js/parseFloat转成数值再存到全局db,输入过程中产生的合法中间状态(比如输入16.5时临时出现的16.、输入负数时临时出现的-)会被解析成和原值一致的数值,无法触发db更新,最终渲染时这些中间状态就会被原值覆盖,表现为输入的小数点被吞。
可行方案
优先选UI临时状态和业务状态分离的实现,完全不需要改动现有业务层的数值计算逻辑,侵入性最低:
- 把输入过程中的临时字符串状态存在组件本地,不要直接同步到全局re-frame db。只有当输入内容解析为合法有效数值时,才触发事件更新全局db中存储的业务数值;输入框的value直接绑定本地存储的临时字符串,保证用户输入的内容不会被意外覆盖。
参考实现代码:(defn apple-size-input [] ;; 初始化本地临时值,和全局数值保持同步 (let [local-input (reagent/atom (str @(rf/subscribe [::apple-size])))] (fn [] [:input {:type :text :value @local-input :on-change (fn [e] (let [raw-str (.. e -target -value) parsed (js/parseFloat raw-str)] ;; 先更新本地临时值,保证输入内容实时显示 (reset! local-input raw-str) ;; 仅当解析结果是有效有限数值,且和当前全局值不一致时,更新全局业务状态 (when (and (js/isFinite parsed) (not= parsed @(rf/subscribe [::apple-size]))) (rf/dispatch [:update-apple-size parsed])))) ;; 输入框失焦时,如果当前输入是非法值,回退到全局存储的合法数值 :on-blur (fn [] (reset! local-input (str @(rf/subscribe [::apple-size]))))}]))) - 如果项目要求输入状态必须全局可访问,可以在全局db中给对应数值字段增加配套的原始输入字符串字段,比如将db结构调整为:
输入时优先更新原始字符串字段,解析出合法有效数值时再同步更新数值字段,输入框绑定原始字符串字段,所有业务计算逻辑还是读原来的数值字段,现有代码完全不需要做兼容修改。{:apple 16 :apple-input-raw "16"}
不推荐方案
不要直接把全局db中所有相关字段改成字符串存储:这种方案会把UI层的输入状态校验逻辑扩散到所有业务计算位置,所有用到该值做加减乘除、比较大小的地方都要额外加解析、判空、处理非法输入的逻辑,改动范围大,后续维护成本极高。
另外不要试图通过把input类型改成number绕过问题,这类输入在不同浏览器下的中间值处理逻辑不一致,部分浏览器会在输入16.这类中间值时直接返回空字符串作为value,反而会引入更多兼容性问题。
内容的提问来源于stack exchange,提问作者Shawn Zhang
相关产品推荐
相关产品推荐

