remix-validated-form重载默认值异常问题咨询
使用remix-validated-form时表单默认值更新异常的问题
我用remix-validated-form管理多字段表单,根据下拉框的change事件重载路由获取表单默认值,但表单无法正确设置新值——每次路由重载后,表单显示的都是上一次的旧默认值,而Remix原生Form则能正常工作。这是我的操作问题还是库的问题?我做了一个可复现的示例。
Loader代码
export const loader = async ({request}: LoaderArgs) => { const url = new URL(request.url); const value = url.searchParams.get('select') || ''; const key = Math.random(); let values = {select: '', fieldA: '', fieldB: '', key}; if (value !== '') { values = {select: value, fieldA: `${value}-A`, fieldB: `${value}-B`, key}; } return json({values}); };
路由代码
export default function RemixValidatedFormRoute() { const data = useLoaderData<typeof loader>(); const navigate = useNavigate(); const {getInputProps: selectProps} = useField('select', {formId: 'fT'}); const {getInputProps: firstInputProps} = useField('fieldA', {formId: 'fT'}); const {getInputProps: secondInputProps} = useField('fieldB', {formId: 'fT'}); const validator = withZod( Z.object({select: Z.string(), fieldA: Z.string(), fieldB: Z.string()}), ); return ( <div> <ValidatedForm id="fT" validator={validator} defaultValues={data.values} key={data.values.key} > <select {...selectProps({ id: 'select', className: 'select', onChange: event => { const value = event.target.value; navigate(`/test?select=${value}`, {replace: true}); }, })} > <option value=""> Select one</option> <option value="1"> First</option> <option value="2"> Second</option> <option value="3"> Third</option> </select> <input {...firstInputProps({ id: 'fieldA', className: 'fieldA' })} /> <input {...secondInputProps({ id: 'fieldB', className: 'fieldB' })} /> </ValidatedForm> </div> ); }
说明
- 可测试Remix原生Form的正常工作情况
- 复现步骤:
- 选择“First”→ 值已加载但下拉框未变更
- 选择“Third”→ 下拉框值更新为“First”
- 选择“Second”→ 所有输入框显示“Third”对应的值
问题原因与修复方案
核心原因
remix-validated-form的defaultValues仅在组件首次挂载时生效,后续defaultValues更新不会自动同步到表单状态。同时你手动处理下拉框onChange并触发导航,导致表单内部状态与loader返回的新值不同步,出现延迟更新的问题。
修复方案一:监听数据变化手动重置表单
通过useFormContext获取表单重置方法,监听loader返回值的变化,主动将表单状态重置为新的默认值:
export default function RemixValidatedFormRoute() { const data = useLoaderData<typeof loader>(); const navigate = useNavigate(); // 获取表单上下文的重置方法 const { reset } = useFormContext({ formId: 'fT' }); // 当loader返回的values变化时,重置表单 useEffect(() => { reset(data.values); }, [data.values, reset]); const {getInputProps: selectProps} = useField('select', {formId: 'fT'}); const {getInputProps: firstInputProps} = useField('fieldA', {formId: 'fT'}); const {getInputProps: secondInputProps} = useField('fieldB', {formId: 'fT'}); const validator = withZod( Z.object({select: Z.string(), fieldA: Z.string(), fieldB: Z.string()}), ); return ( <div> <ValidatedForm id="fT" validator={validator} defaultValues={data.values} // 用select值作为key,比随机数更稳定 key={data.values.select} > <select {...selectProps({ id: 'select', className: 'select', onChange: event => { const value = event.target.value; navigate(`/test?select=${value}`, {replace: true}); }, })} > <option value=""> Select one</option> <option value="1"> First</option> <option value="2"> Second</option> <option value="3"> Third</option> </select> <input {...firstInputProps({ id: 'fieldA', className: 'fieldA' })} /> <input {...secondInputProps({ id: 'fieldB', className: 'fieldB' })} /> </ValidatedForm> </div> ); }
修复方案二:改用表单提交触发路由更新
如果业务允许,移除手动导航逻辑,通过表单提交触发路由变化,让表单状态与loader返回值自动同步:
// 移除select的手动onChange,仅保留表单字段绑定 <select {...selectProps({ id: 'select', className: 'select', })} > {/* 选项不变 */} </select> // 给ValidatedForm添加action属性,通过表单提交触发路由action,再由action重定向到带参数的路由 // 同时调整loader和action逻辑,确保参数传递正确
内容的提问来源于stack exchange,提问作者Hernandcb
相关产品推荐
相关产品推荐

