You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SolidJS中FormContext无法传递给子组件问题求助

问题原因与修复方案

核心问题

你的代码中存在两个关键问题导致Context无法正确传递:

  1. FormContext默认值与实际值类型不匹配:创建Context时默认值的data是普通对象,但实际传递给Provider的data是SolidJS Signal的Accessor函数,类型不匹配导致Context识别异常。
  2. InputText中错误访问Signal数据:直接用form.data[name]访问Signal,而正确方式应该调用Accessor函数form.data()[name]。

修复步骤

1. 修正FormContext默认值与接口类型

在FormContextProvider.tsx中,调整Form接口和Context默认值,确保类型统一:

// 修正Form接口,明确data为Accessor类型
export interface Form {
  data: Accessor<FormData>;
  set: (data: FormData | ((prev: FormData) => FormData)) => void;
  validationResult: Accessor<Joi.ValidationResult<any> | undefined>;
  touched: Accessor<TouchedData>;
  setFieldValue: (field: string, value: string) => void;
  inputChangeHandler: (name: string) => (e: Event) => void;
}

// 修正Context默认值,data改为返回空对象的函数
export const FormContext = createContext<Form>({
  data: () => ({}),
  set: () => undefined,
  validationResult: () => undefined,
  touched: () => ({}),
  setFieldValue: () => undefined,
  inputChangeHandler: () => () => undefined,
});

2. 修正setFieldValue函数(避免覆盖表单数据)

在FormContextProvider.tsx的form对象中,修改setFieldValue为合并式更新:

setFieldValue: (field: string, value: string) => {
  setFormData(prev => ({ ...prev, [field]: value }));
},

3. 修正initialTouchData初始化

在FormContextProvider.tsx中,将初始触摸数据改为空对象:

const initialTouchData: TouchedData = {};

4. 修正InputText中访问表单数据的方式

在InputText.tsx中,修改value的取值方式:

<input 
  type={type} 
  name={name} 
  id={name} 
  placeholder={label} 
  value={form.data()[name] || ""} 
  onInput={form.inputChangeHandler(name)} 
/>

5. 验证Context导入路径

确保InputText.tsx中导入的FormContext与FormContextProvider.tsx导出的是同一个实例,检查导入路径是否正确:

import { FormContext } from "../FormContextProvider/FormContextProvider";

修正后的关键代码片段

FormContextProvider.tsx(核心部分)

// ...其他代码
export default function FormContextProvider(props: FormContextProviderProps) {
  const { children, initialData, validation, postValidation = defaultPostValidation } = props;
  const [formData, setFormData] = createSignal<FormData>(initialData || {});
  const initialTouchData: TouchedData = {};
  const [touched, setTouched] = createSignal<TouchedData>(initialTouchData);
  const [validationResult, setValidationResult] = createSignal<Joi.ValidationResult<any> | undefined>();

  // ...validate函数保持不变

  const form = {
    data: formData,
    set: setFormData,
    validationResult,
    touched,
    setFieldValue: (field: string, value: string) => {
      setFormData(prev => ({ ...prev, [field]: value }));
    },
    inputChangeHandler: (name: string) => (e: Event) => {
      const target = e.target as HTMLInputElement;
      setFormData(prev => ({ ...prev, [name]: target.value }));
      setTouched(prev => ({ ...prev, [name]: true }));
      validate();
    },
  };
  validate();
  return (
    <FormContext.Provider value={form}>
      <form use:formDecorator>{children}</form>
    </FormContext.Provider>
  );
}

InputText.tsx(核心部分)

export default function InputText(props: InputTextProps) {
  const { label, name, type } = props;
  const form = useContext(FormContext);
  if (!form) {
    return <div>FormContextProvider not found</div>;
  }

  const [error, setError] = createSignal("");

  createEffect(() => {
    let updatedError = "";
    const errorDetails = form.validationResult()?.error?.details;
    if (!errorDetails || !errorDetails.length) return setError(updatedError);
    errorDetails.forEach((error) => {
      if (error.context?.key === name) {
        updatedError = error.message;
      }
    });
    setError(updatedError);
  });

  return (
    <div>
      <input 
        type={type} 
        name={name} 
        id={name} 
        placeholder={label} 
        value={form.data()[name] || ""} 
        onInput={form.inputChangeHandler(name)} 
      />
      <div class="error">{form.touched()[name] && error()}&nbsp;</div>
    </div>
  );
}

内容的提问来源于stack exchange,提问作者RobKohr

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 19:10:54