如何将表单数据从孩子读入父母?

时间:2019-04-05 20:21:27

标签: reactjs ecmascript-6 react-final-form

我正在使用手工制作的表单生成器,并使用React Final Form来管理状态和数据。现在的问题是,我需要从表单外部的组件中读取数据,甚至在提交数据之前向用户显示输入外观的实际状态。

import React, { Component } from 'react'
import PropTypes from 'prop-types';
import { I18n } from 'react-i18nify';
import * as INPUTTYPES from '../../constants/inputTypes';
import { CONCAT_ID_BASES } from '../../constants/config';
import 'babel-polyfill';
import { Form, Field } from 'react-final-form'
const weblog = require('webpack-log');
const log = weblog({ name: 'wds' }) // webpack-dev-server

class FormGenerator extends Component {
    static propTypes = {
        fields: PropTypes.any,
        prefix: PropTypes.any,
        children: PropTypes.any
    }

    state = {
        data: {}
    }

    sleep = ms => new Promise(resolve => setTimeout(resolve, ms))

    onSubmit = async values => {
        await this.sleep(300)
        window.alert(JSON.stringify(values, 0, 2))
    }

    static simpleMemoize = fn => {
        let lastArg
        let lastResult
        return arg => {
            if (arg !== lastArg) {
                lastArg = arg
                lastResult = fn(arg)
            }
            return lastResult
        }
    }

    static textValidate = FormGenerator.simpleMemoize(async value => {
        if (!value) {
            return I18n.t('error-no-text-written');
        }
        //await sleep(400)
        if (value.trim().length() > 0) {
            return I18n.t('error-no-text-found');
        }
    })

    static createInput = (newKey, value, validate) => {
        let data = {
            type: value.type,
            //disabled: typeof value.editable !== "undefined" ? !value.editable : false,
            className: "form-control",
            id: `${newKey}`,
            value: value.value
        }
        return <Field key={newKey} name={data.id} validate={validate}>
            {({ input, meta }) => (
                <div className="form-group col-md-6">
                    <label htmlFor={`${newKey}`}>{I18n.t(`${newKey}`)}</label>
                    <input {...data} {...input} />
                    {meta.error && meta.touched && <span>{meta.error}</span>}
                </div>
            )}
        </Field>
    }

    static createSelectInput = (newKey, value) => {
        let data = {
            type: value.type,
            disabled: typeof value.editable !== "undefined" ? !value.editable : false,
            className: "form-control",
            id: `${newKey}`,
            value: value.value
        }
        return <React.Fragment key={newKey}>
            <div className="form-group col-md-6">
                <label htmlFor={`${newKey}`}>{I18n.t(`${newKey}`)}</label>
                <input {...data} />
            </div>
        </React.Fragment>
    }

    initialValues = function () {
        let { prefix, fields } = this.props;
        prefix = prefix ? prefix + CONCAT_ID_BASES : '';
        fields ? fields.map((field) => {
            const newKey = `${prefix}${field.key}`
            this.setState((prevState) => {
                let newData = { ...prevState.data };
                newData[newKey] = field.value.value;
                return { data: newData };
            })
        }) : null;
    }

    componentDidMount() {
        this.initialValues();
    }

    componentDidUpdate() {
        //console.log(this.state)
        //this.props.suscribeCallback(values)
    }

    inputGenerator(field, prefix) {
        const { key, value } = field;
        const { type } = value;
        const textValidate = FormGenerator.textValidate;
        const newKey = `${prefix}${key}`
        let element = null;
        const createInput = FormGenerator.createInput;

        switch (true) {
            case new RegExp(INPUTTYPES.TEXT.join("|"), "i").test(type):
                value.type = "text";
                element = createInput(newKey, value, textValidate)
                break;
            case new RegExp(INPUTTYPES.NUMBER.join("|"), "i").test(type):
                value.type = "number";
                element = createInput(newKey, value, textValidate)
                break;
            case new RegExp(INPUTTYPES.SELECT.join("|"), "i").test(type):
                break;
            default:
                log.error("DATA NOT ITENDIFIED TYPE:" + type, key, value);
                break;
        }
        return element;
    }

    render() {
        let fields = this.props.fields;
        let { prefix } = this.props;
        prefix = prefix ? prefix + CONCAT_ID_BASES : ''
        const { inputGenerator, onSubmit } = this;

        return (
            <Form
                onSubmit={onSubmit}
                initialValues={this.state.data}
                render={({ values }) => {
                    return <div className="form-row">
                        {fields.map((field) => {
                            return inputGenerator(field, prefix);
                        })}
                        <pre>{JSON.stringify(values, 0, 2)}</pre>
                    </div>
                }} />
        )
    }
}

export default FormGenerator;

并这样称呼它:

{
                detalles ? (() => {
                  return <FormGenerator
                    suscribeCallback={this.formDataChange}
                    prefix={this.props.prefix}
                    fields={detalles} />
                })() : null
              }

但是现在的问题是,我需要在values之外阅读<Form/>,以便可以在其父目录中阅读。

如果我包含一个回叫并将其扔到render方法this.props.suscribeCallback(values)中,它将尝试对其进行过多调用,以致使网站崩溃。当然,这不是有效的解决方案,但我不知道如何解决。

我是Reactjs的新手,如果这是初学者的错误,请表示歉意

2 个答案:

答案 0 :(得分:0)

假设您有一个名为SignUp的父组件,并且有一个名为SignUpForm的子组件,一个好的解决方案是将所有表单信息都包含在该子组件中,然后调用{{1}完成后,在父级中起作用。但是,如果您真的想操纵父母的状态进入您的孩子,它将看起来像这样:

submit()

就在父母身上!我们定义了状态并传递了一个让孩子能够更新父母状态的函数。现在在孩子身上:

class SignUp extends Component{
    state = {
        controls:{
            email:{value:'', validation:[/*...*/]},
            pass :{value:'', validation:[/*...*/]
        }
    }

    onChangeText = (key, value) =>{
        this.setState(state =>({
            ...state,
            controls:{
                ...state.controls,
                [key]: {
                    ...state.controls[key],
                    value 
           }
        }))
    }

    render(){
        return(
            <SignUpForm onChangeText={this.onChangeText} />
        )
    }
}

现在,子级上的每个class SignUpForm extends Component{ render(){ return( <input onChange={e => this.props.onChangeText('email', e.target.value)} /> <input onChange={e => this.props.onChangeText('pass', e.target.value)} /> ) } } 事件都会更新父级上控件的值。

答案 1 :(得分:0)

如果您需要表单之外的表单数据,我建议使用类似Redux Example中探索的模式的工具,其中FormStateToRedux组件正在侦听表单状态的更改并将其发送。 ..某处。不必是Redux。