#reactjs
#reactjs
Вопрос:
У меня есть эти 2 компонента
FormComponent
const FormComponent = (props) => {
//keep the current step in the component state
const [currentStep, setCurrentStep] = useState(props.step);
const [componentData, setComponentData] = useState(null);
const [componentQuestions, setQuestions] = useState(null);
const [errors, setErrors] = useState();
const updateUserResponse = props.updateUserResponse;
const renderStepsTracker = props.renderStepsTracker;
/**
* Get the component content after mount
*/
useEffect(() => {
if (componentData) {
setQustionsData(componentData);
}
//fire if componentData has changed or error object is changed
}, [errors,componentData]);
/**
* Get the component content after mount
*/
useEffect(() => {
if (currentStep amp;amp; !componentData) {
let stepData = currentStep.stepdata;
stepData.update = getUpdateTime();
setComponentData(stepData);
}
});
/**
* Get the current time in milliseconds
*/
const getUpdateTime = () => {
var d = new Date();
var n = d.getTime();
return n
}
/**
* Extract the questions from the step object and
* set the state accordingly
*/
const setQustionsData = (stepData) => {
let questions = stepData.questions;
//assign a react component to each question
questions.map((question, key) => {
const component = getComponent(question);
const question_id = question.question_id;
questions[key].component = component;
questions[key].error = (null != errors amp;amp; 'undefined' !== typeof errors[question_id]) ? errors[question_id] : '';
})
setQuestions(questions);
}
/**
* a callback function to validate the current form
* @param {object} step
*/
const validateForm = (step) => {
const formErrors = props.validateForm(step);
setErrors(formErrors)
}
/**
* Render the form
*/
const renderForm = () => {
return null !== componentQuestions ? (
<>
<div className="row full-height">
<div className="column">
<div className="form-wrapper">
<div className="questions">
{componentQuestions.map(question => {
return (
<>
{question.component}
</>
)
})}
</div>
<NextStepButton
step={currentStep}
nextStepCallback={props.jumpToStep}
validateForm={validateForm}
/>
</div>
</div>
</div>
</>
) : ''
}
/**
* Render the image in case it exists
*/
const renderImage = () => {
return (
<div className="full-height">
<img className="full-height-image" src={componentData.image} alt="" />
</div>
)
}
/**
* Get the question component
*/
const getComponent = (question) => {
switch (question.question_type) {
case "text":
return (
<div className="field-wrap">
<label>
<span className="question-label">{question.question_label}</span>
<span className="question-title">{question.question_title}</span>
<input type="text"
name={question.question_id}
onChange={updateUserResponse}
required={question.required}
/>
{question.error ?
<span className={"error-message " question.error.reason}>{question.error_message}</span>
: ''}
</label>
</div>
)
}
}
return (
componentData ?
<>
<div className="row full-height expanded">
{componentData.image ?
<div className="column full-height col-collapse image-wrap">
{renderImage()}
</div>
: ''}
<div className="column full-height questions-container">
{renderStepsTracker()}
{renderForm()}
</div>
</div>
</>
: ''
)} enter code hereexport default FormComponent
**
- NextStepButton
**
const NextStepButton = (props) => {
const step = props.step
const validateForm = props.validateForm
return props.step.nextButtonText ? (
<span className="next-step-button" onClick={() => {
validateForm( step )
}}>
{step.nextButtonText}
</span>
) : ''
}
export default NextStepButton
Когда пользователь нажимает на следующий шаг, форма проверяется и возвращает массив ошибок,
Массив ошибок устанавливается в состояние компонента, а затем должен отображать ошибку
По какой-то причине ошибка появляется только после того, как пользователь нажимает 3 раза на кнопку следующего шага
Почему это так?
РЕДАКТИРОВАТЬ это основной компонент, который содержит глобальную проверку формы
const MainContainer = () => {
//set state variables
const [nextButtonText, setNextButtonText] = useState()
const [previousButtonText, setPreviousButtonText] = useState()
const [backButtonCls, setBackButtonCls] = useState()
const [currentStep, setCurrentStep] = useState()
const [stepsData, setStepsData] = useState()
const [currentStepNum, setCurrentStepNum] = useState(null)
const [classNames, setclassNames] = useState(null)
const [userData, setUserData] = useState({})
//set refrence for callbacks that use this component state
const ref = useRef({
stepsData: stepsData,
currentStep: currentStep
})
/**
* On Initial state
*/
useEffect(() => {
if (!stepsData) {
getStepsData().then((response) => {
initProcess(response)
})
}
})
/**
* Init the process after getting data from the server
*/
const initProcess = (response) => {
const steps = setStepsComponents(response.data.steps)
const initialStep = steps[0];
ref.current.stepsData = steps
setStepsData(steps)
setStep(initialStep, 1)
}
/**
* Form submission callback
* Loop over step questions and check for the appropriate userdata
*/
const validateForm = (submittedStep) => {
let errors = {};
if ('null' !== typeof submittedStep.stepdata.questions) {
const questions = submittedStep.stepdata.questions
questions.map((question, key) => {
const question_name = question.question_id
//if there is an error add it to the questions collection
if (question.required amp;amp; !userData[question_name]) {
errors[question.question_id] = {
reason : 'required'
}
}
})
}
if (errors) {
return errors
}
return true;
}
/**
* Capture user settings on change
*/
const updateUserResponse = (event) => {
userData[event.target.name] = event.target.value;
setUserData(userData)
}
/**
* Set the appropriate step component
* @param {OBJECT} steps
*/
const setStepsComponents = (steps) => {
steps.map((step, key) => {
switch (steps[key]['component']) {
case "onboarding":
steps[key]['component'] = <Onboarding step={step} setUserData />
break;
case "questions":
steps[key]['component'] = <FormComponent
step={step}
renderStepsTracker={renderStepsTracker}
updateUserResponse={updateUserResponse}
validateForm={validateForm}
/>
break;
}
})
return steps
}
/**
* Callback function that creates the steps tracker on each component
*/
const renderStepsTracker = () => {
return ref.current.stepsData ? (
<>
<div className="stepsTracker">
<div className="row">
<div className="column">
<div className="stepsTrackerWrap">
{ref.current.stepsData.map((step, key) => {
const activeClass = ref.current.currentStep === key ? "active" : ""
return key > 0 ? (
<>
<div className={"stepTracker " activeClass}>
<div className="stepTrackerImage">
<img src={step.icon} alt="" />
</div>
<label>{step.stepdata.title}</label>
</div>
<div className="stepSpacer"></div>
</>
) : ''
})}
</div>
</div>
</div>
</div>
</>
) : ''
}
/**
* Render the steps component if data is available
*/
const renderSteps = () => {
return stepsData ? (
<StepZilla
steps={stepsData}
onStepChange={onstepChange}
nextButtonText={nextButtonText}
showSteps={false}
backButtonText={previousButtonText}
backButtonCls={backButtonCls}
/>
) : ''
}
/**
* Set the data for the current step
* @param {object} step
*/
const setStep = (step, stepNum) => {
setNextButtonText(step.showNextButton ? step.nextButtonText : true)
setPreviousButtonText(step.previousButtonText)
setBackButtonCls(step.backButtonCls)
setCurrentStep(step)
setCurrentStepNum(stepNum)
ref.current.currentStep = stepNum
//add bottom padding in case the next button appears
if (step.showNextButton) {
setclassNames('padding')
} else {
setclassNames(' ')
}
}
/**
* Perform actions when step is changed
* @param {step} step
*/
const onstepChange = (stepNum) => {
if ('undefined' === typeof stepNum) {
stepNum = '0';
}
setCurrentStepNum(stepNum)
const nextStep = stepsData[stepNum];
setStep(nextStep, stepNum)
}
return (
<>
<div className={'step-progress ' classNames}>
{renderSteps()}
</div>
</>
)
}
export default MainContainer
Комментарии:
1. Что такое
props.validateForm
? КудаvalidateForm
передается в качестве реквизитаFormComponent
? Можете ли вы обновить свой вопрос, чтобы включить весь соответствующий код?2. Обновлен основной компонент контейнера
3. Можете ли вы воссоздать его в песочнице?
4. Согласен, слишком много кода, который нужно просеивать и отслеживать, не понимая, как все это должно сочетаться и работать. Работающий codesandbox был бы отличным. Мне удалось вручную выполнить пошаговый код до
submittedStep.stepdata.questions
validateForm
ввода. Если'null' !== typeof submittedStep.stepdata.questions
условие всегда выполняетсяfalse
, вы всегда возвращаете пустой{}
объект ошибки () обратно в форму. (На самом деле вы всегда возвращаете объект ошибки независимо от того, на самом деле ) Возможно, вы хотели проверитьif (Object.keys(errors).length) return errors
.5. Ваш код кажется излишне запутанным, и, честно говоря, это анти-шаблон для хранения «компонентов» / «JSX» в состоянии реакции, т.Е.
componentQuestions
. Я протестировал это, непосредственно выполнив рендерингshowError(question.question_id)
renderForm
вместоquestion.component
, и ошибка «test» отображается после первого нажатия кнопки «Нажмите здесь, чтобы проверить» (FormComponent:90). Кажется, что когда вы задаете данные вопроса и получаете компонент, ошибка компонента — это цикл рендеринга (вероятно, из-за чрезмерно сложных обновлений состояния.