React Context Provider存储的回调状态为undefined问题
问题描述
已查阅平台内其他开发者提出的同类问题,均未找到适配当前业务场景的解决方案。
项目包含公共布局组件,布局内置工具栏,负责触发表单提交、取消操作。为实现工具栏跨组件控制条件渲染表单的提交/取消逻辑,采用基于Context.Provider实现的FormProvider组件包裹整个布局,在Context状态变量中存储表单提交、取消的回调函数,逻辑如下:
- 条件渲染加载的表单组件调用Context暴露的赋值方法,将表单提交/取消回调存入状态
- 工具栏组件读取Context中的状态变量,调用对应回调执行操作
实际运行出现以下异常:
- 从Context中读取到的存储回调的状态变量始终为
undefined - 日志显示调用
setOnSubmit方法赋值后,onSubmit值未按预期更新,监听onSubmit变化的useEffect钩子仅在首次渲染时触发 - 点击工具栏保存按钮调用
submit方法时,方法内部捕获的onSubmit值永远为undefined
业务代码片段
FormProvider
type FormContextProps = { setOnSubmit: (callable: Function | undefined) => void assignOnSubmit: Dispatch<any> setOnCancel: (callable: Function | undefined) => void submit: (e: any) => void cancel: () => void } const initAuthContextPropsState = { setOnSubmit: () => { }, assignOnSubmit: () => { }, setOnCancel: () => { }, submit: (e: any) => { }, cancel: () => { }, } const FormContext = createContext<FormContextProps>(initAuthContextPropsState) const useTForm = () => { return useContext(FormContext) } const FormProvider: FC = ({ children }) => { const [onSubmit, assignOnSubmit] = useState<Function | undefined>() const [onCancel, assignOnCancel] = useState<Function | undefined>() const setOnSubmit = (callable: Function | undefined) => { console.log('setOnSubmit', callable) assignOnSubmit(callable) console.log('setOnSubmit after', onSubmit) } const setOnCancel = (callable: Function | undefined) => { assignOnCancel(callable) } useEffect(() => { console.log("changed onSubmit"); // 该钩子仅在首次渲染时触发 }, [onSubmit]) const submit = (e: any) => { console.log('form submited', onSubmit) // 点击工具栏保存按钮时此处始终为undefined if (onSubmit) onSubmit(e) } const cancel = () => { if (onCancel) onCancel() } return ( <FormContext.Provider value={{ setOnSubmit, assignOnSubmit, setOnCancel, submit, cancel }}> {children} </FormContext.Provider> ) }
Toolbar
const FormToolbar: FC = () => { const {classes} = useLayout() const {submit, cancel} = useTForm() const submitForm = (e: any) => submit(e) return ( <div className='toolbar' id='kt_toolbar'> {/* begin::Container */} <div id='kt_toolbar_container' className={clsx(classes.toolbarContainer.join(' '), 'd-flex flex-stack')} > <DefaultTitle /> {/* begin::Actions */} <div className='py-1 d-flex align-items-center'> {/* begin::Button */} <button className='btn btn-sm btn-primary me-4' onClick={submitForm} > Save </button> <button className='btn btn-sm btn-primary' onClick={cancel} > Cancel </button> {/* end::Button */} </div> {/* end::Actions */} </div> {/* end::Container */} </div> ) }
EditForm.tsx
const EditForm: React.FC<Props> = () = { const form = useRef() as React.MutableRefObject<HTMLFormElement> const { setOnSubmit, assignOnSubmit } = useTForm() useLayoutEffect(() => { setOnSubmit(() => { form?.current.dispatchEvent(new Event('submit')) }); console.log('Form changed', form) }, [form]) return ( <form onSubmit={formik.handleSubmit} ref={form}> // ... </form> ); }
Main Component
function StaffManagement({ user, selectedLanguageId, idMenu }: Props) { const [editing, setEditing]: [any, Function] = useState(null) return ( <div className='row'> <div className="col-lg-4"> <ModuleItemList className='card-xxl-stretch mb-xl-3' edit={setEditing} /> </div> <div className="col-lg-8"> {editing && <EditForm userId={user.id} menuId={idMenu} />} </div> </div> ) }
内容的提问来源于stack exchange,提问作者Sagar Guhe
相关产品推荐
相关产品推荐

