취업/React.JS

[react] formik 라이브러리 동적 할당하기.

카슈밀 2023. 6. 2. 20:39
반응형

진짜 엿같은 라이브러리다.

정적은 쉬우나 동적 할당하기가 너무 어려웠다.

 

쟁점.

1. initialState 설정하기.

2. validationSchema 설정하기.

import React, { useEffect, useState } from "react";
import {
  Card,
  CardBody,
  Col,
  Container,Form, FormFeedback, FormGroup, Input, Label, Row
} from "reactstrap";
import * as Yup from "yup";

import { useFormik } from 'formik';

// redux

const components = props => {
    const dispatch = useDispatch();
    const [ dynamicInput, setDynamicInput ] = useState([]);
    const [validationSchema, setValidationSchema] = useState(null);

    
    const validation = useFormik({
        enableReinitialize: true,
            
        initialValues: {},
        validationSchema: validationSchema,
        onSubmit: async (values) => {
            let data = {
                rqdata : values
            }

            try {
                // 수정 성공시 대충 api
            } catch (e) {
                console.log(e);
                // 통신 실패.
                return;
            }
        }
    });
    
    const getDeafaultSettings = async () => {
        let data = {
        }
        try {
            let ret = await authAxios().get('www.example.com', { params : data });
            if(ret?.result == 'Y') {
                setDynamicInput(ret.data);

                // Update the validation schema with the new dynamicInput
                // useFormik 스키마 동적할당.
                const updatedValidationSchema = Yup.object().shape(
                    ret.setting_list.reduce((schema, el) => {
                      return {
                        ...schema,
                        [el.name]: Yup.number().required(`Please Enter ${el.name}`),
                      };
                    }, {})
                );
            
                setValidationSchema(updatedValidationSchema); // Update the validation schema in useFormik

                // Update the form values
                // initial 값 설정.
                validation.setValues(ret.data.reduce((values, el, i) => {
                  return {
                    ...values,
                    [i]: el, // or set any default value if needed
                  };
                }, {}));
                return;
            } else {
                // 통신 실패
            }
        } catch (e) {
            // 통신 실패
        }
    }

    useEffect(()=> {
        getDeafaultSettings();
    }, []);

    return (
      <React.Fragment>
        <div className="page-content">
            <Container fluid>
                <Form className="needs-validation"
                    onSubmit={(e) => {
                        e.preventDefault();
                        validation.handleSubmit();
                        return false;
                    }}>
                    <Col xl={12}>
                        <Card>
                            <CardBody>
                                {dynamicInput && dynamicInput.map((el, i) => {
                                    return (
                                        <Row key={el.id}>
                                            <FormGroup className="mb-3">
                                                <Label htmlFor={el.name}>{el.description}</Label>
                                                <Input
                                                    name={el.name}
                                                    placeholder={`Enter the ${el.name}`}
                                                    type="number"
                                                    className="form-control"
                                                    onChange={(e) => {
                                                        validation.setFieldValue(`${i}.value`, e.target.value);
                                                        validation.handleChange(e);
                                                    }}
                                                    onBlur={validation.handleBlur}
                                                    value={validation.values[i].value || ""} // 해당 내용에서 값을 보고 진행해야함. initial에서 꼬이는 바람에 해당 내용의 i값을 보고 진행한다.
                                                    invalid={(validation.touched?.[el.name]) && (validation.errors?.[el.name]) ? true : false}
                                                />
                                                {validation.touched?.[el.name] && validation.errors?.[el.name] ? (
                                                    <FormFeedback type="invalid">{validation.errors?.[el.name]}</FormFeedback>
                                                ) : null}
                                            </FormGroup>
                                        </Row>
                                        );
                                    })
                                }
                                <div className='d-flex justify-content-end'>
                                    <button
                                        type="submit"
                                        className="btn btn-primary  float-right"
                                    >
                                        Save changes
                                    </button>
                                </div>
                            </CardBody>
                            
                        </Card>
                        
                    </Col>

                </Form>
            </Container>
          </div>
    </React.Fragment>
    );
};

export default components;

 

스키마 동적할당도 어렵고, 해당 내용을 겨우 진행하니,

initialValues가 문제를 일으켜 state를 폼필드를 추적하지 못하는 문제를 일으켰다.

 

++ 

해당 내용을 겨우 진행하니, setValues로 응답은 되나 버튼 저장이 작동안된다.

  useEffect(() => {
    const handleFormSubmit = (e) => {
      e.preventDefault();
      validation.handleSubmit(); // validation.handleSubmit 호출
    };

    const form = document.getElementById("form");
    form.addEventListener("submit", handleFormSubmit);

    return () => {
      form.removeEventListener("submit", handleFormSubmit);
    };
  }, []);

해당 내용으로 바인딩 시켜준다.

해당 코드로 작동해야만 문제없이 작동한다.

const form에 담지 않고 다이렉트로 꽂으면 해당 form을 페이지 떠났을때 파괴되서 페이지 오류가 발생한다.

 

이렇게 작성했더니, button 클릭시 페이지 접속후 바로 버튼 클릭시 작동되지 않는다.

최초 initialValues를 {}로 했기에 바로 {}로 입력되는 문제가 발생하는 것.

import React, { useEffect, useState } from "react";
import {
  Card,
  CardBody,
  Col,
  Container,Form, FormFeedback, FormGroup, Input, Label, Row
} from "reactstrap";
import * as Yup from "yup";

import { useFormik } from 'formik';

// redux

const components = props => {
    const dispatch = useDispatch();
    const [ dynamicInput, setDynamicInput ] = useState([]);
    const [validationSchema, setValidationSchema] = useState(null);
    const [dynamicInput, setDynamicInput] = useState([]);
    
    const validation = useFormik({
        enableReinitialize: true,
            
        initialValues: dynamicInput, // 요렇게 바꿔준다.
        validationSchema: validationSchema,
        onSubmit: async (values) => {
            let data = {
                rqdata : values
            }

            try {
                // 수정 성공시 대충 api
            } catch (e) {
                console.log(e);
                // 통신 실패.
                return;
            }
        }
    });
    
    const getDeafaultSettings = async () => {
        let data = {
        }
        try {
            let ret = await authAxios().get('www.example.com', { params : data });
            if(ret?.result == 'Y') {
                setDynamicInput(ret.data);

                // Update the validation schema with the new dynamicInput
                // useFormik 스키마 동적할당.
                const updatedValidationSchema = Yup.object().shape(
                    ret.setting_list.reduce((schema, el) => {
                      return {
                        ...schema,
                        [el.name]: Yup.number().required(`Please Enter ${el.name}`),
                      };
                    }, {})
                );
            
                setValidationSchema(updatedValidationSchema); // Update the validation schema in useFormik

                // Update the form values
                // initial 값 설정.
                validation.setValues(ret.data.reduce((values, el, i) => {
                  return {
                    ...values,
                    [i]: el, // or set any default value if needed
                  };
                }, {}));
                return;
            } else {
                // 통신 실패
            }
        } catch (e) {
            // 통신 실패
        }
    }

    useEffect(() => {
        getDeafaultSettings();
    }, []);

    useEffect(() => {
        if (isInitialized) {
            const form = document.getElementById("form");
            form.addEventListener("submit", handleFormSubmit);
    
            return () => {
                form.removeEventListener("submit", handleFormSubmit);
            };
        }
    }, [isInitialized]); // isInitialized 상태에 따라 useEffect 실행

    return (
      <React.Fragment>
        <div className="page-content">
            <Container fluid>
                <Form className="needs-validation"
                    onSubmit={(e) => {
                        e.preventDefault();
                        validation.handleSubmit();
                        return false;
                    }}>
                    <Col xl={12}>
                        <Card>
                            <CardBody>
                                {dynamicInput && dynamicInput.map((el, i) => {
                                    return (
                                        <Row key={el.id}>
                                            <FormGroup className="mb-3">
                                                <Label htmlFor={el.name}>{el.description}</Label>
                                                <Input
                                                    name={el.name}
                                                    placeholder={`Enter the ${el.name}`}
                                                    type="number"
                                                    className="form-control"
                                                    onChange={(e) => {
                                                        validation.setFieldValue(`${i}.value`, e.target.value);
                                                        validation.handleChange(e);
                                                    }}
                                                    onBlur={validation.handleBlur}
                                                    value={validation.values[i].value || ""} // 해당 내용에서 값을 보고 진행해야함. initial에서 꼬이는 바람에 해당 내용의 i값을 보고 진행한다.
                                                    invalid={(validation.touched?.[el.name]) && (validation.errors?.[el.name]) ? true : false}
                                                />
                                                {validation.touched?.[el.name] && validation.errors?.[el.name] ? (
                                                    <FormFeedback type="invalid">{validation.errors?.[el.name]}</FormFeedback>
                                                ) : null}
                                            </FormGroup>
                                        </Row>
                                        );
                                    })
                                }
                                <div className='d-flex justify-content-end'>
                                    <button
                                        type="submit"
                                        className="btn btn-primary  float-right"
                                    >
                                        Save changes
                                    </button>
                                </div>
                            </CardBody>
                            
                        </Card>
                        
                    </Col>

                </Form>
            </Container>
          </div>
    </React.Fragment>
    );
};

export default components;

최종 코드.

이렇게 해야 작동이 잘된다.

 

너무 빠른 속도에 bind되어버리면, 동적코드가 갱신되기전 값에 붙는 경우가 발생하여 통신이 끝난 여부를 감지하고

해당 작업이 끝나면 갱신되게 만들어 바인딩하면 작동되게 했다.

728x90