首页 > 解决方案 > 从父 React 组件状态继承的 Formik initialValues 的异步更新(利用 useEffect 挂钩?)

问题描述

我目前正在用户入职过程中构建一个多步骤表单,这就是为什么我需要将所有表单数据集中在父 React 组件状态中的原因。

我需要使用用户信息更新 initialValues,但这是一个异步过程。

我想创建一个调用 setState 的 useEffect 钩子,但也许有一种更优雅的方法......

将 initialValues 作为 useEffect 依赖项之一似乎会创建一个无限循环 ( Maximum update depth exceeded)。这就是为什么我找到的工作解决方案是在...中复制所有初始值

那么在获取异步用户信息后,如何仅更新 initialValues 中的特定值呢?

这是实现的简化版本:

import React, { useState, useEffect } from 'react'
// Auth0 hook for authentication (via React Context).
import { useAuth0 } from '../../contexts/auth/auth'

import { Formik, Form, Field } from 'formik'

export default () => {
    const { user } = useAuth0()

    const initialValues = {
        profile: {
            name: '',
            address: '',
            // Other properties...
        },
        personalInfo: {
            gender: '',
            birthday: '',
            // Other properties...
        },
    }
    const [formData, setFormData] = useState(initialValues)

    const [step, setStep] = useState(1)
    const nextStep = () => setStep((prev) => prev + 1)

    useEffect(() => {
        const updateInitialValues = (user) => {
            if (user) {
                const { name = '', gender = '' } = user

                const updatedInitialValues = {
                    profile: {
                        name: name,
                        // All other properties duplicated?
                    },
                    personalInfo: {
                        gender: gender,
                        // All other properties duplicated?
                    },
                }

                setFormData(updatedInitialValues)
            }
        }

        updateInitialValues(user)
    }, [user, setFormData])

    switch (step) {
        case 1:
            return (
                <Formik
                    enableReinitialize={true}
                    initialValues={formData}
                    onSubmit={(values) => {
                        setFormData(values)
                        nextStep()
                    }}
                >
                    <Form>
                        <Field name="profile.name" type="text" />
                        <Field name="profile.address" type="text" />
                        {/* Other fields */}
                        <button type="submit">Submit</button>
                    </Form>
                </Formik>
            )
        case 2:
            return (
                <Formik
                    enableReinitialize={true}
                    initialValues={formData}
                    onSubmit={(values) => {
                        setFormData(values)
                        nextStep()
                    }}
                >
                    <Form>
                        <Field name="personalInfo.gender" type="text" />
                        <Field name="personalInfo.birthday" type="text" />
                        {/* Other fields */}
                        <button type="submit">Submit</button>
                    </Form>
                </Formik>
            )
        // Other cases...
        default:
            return <div>...</div>
    }
}

标签: reactjssetstateformikuse-effect

解决方案


看到这个问题对我来说可能已经晚了,我最近恰好在做一个类似的项目。

对于我的用例,我只使用一个 Formik,并使用类似于 Formik 多步表单向导的理论:https ://github.com/formium/formik/blob/master/examples/MultistepWizard.js用于我的多步表单。

在每一步,我都需要获取 API 来预填充数据,我也使用 useEffect 但由于我只是在加载特定步骤时调用 API 一次,所以我强制它的行为与 ComponentDidMount() 相同,即离开[] 注释为空,// eslint-disable-next-line因此不会给我警告。

成功加载数据后,我在 useEffect 中使用 setFieldValue 。我觉得我的也不是处理这种情况的好方法,我刚刚发现了一些可能有用的东西:https ://github.com/talor-hammond/formik-react-hooks-multi-step-form ,它有动态初始值。(虽然是打字稿)

我将参考这个并尝试在我的每个步骤中使用,并且可能使用 Context 或将它们包装在父级中并将数据存储在父级 Formik 中。

并且为您获得无限循环可能是因为setFormData不应该在依赖项中,因为当您设置状态时,组件重新渲染,useEffect 再次调用。

不确定这是否可以帮助您,或者您已经知道如何实现它,我会更深入地研究它。


推荐阅读