#javascript #reactjs
#javascript #reactjs
Вопрос:
у меня есть несколько форм на вкладках, это одна из них, но когда я нажимаю, чтобы просмотреть эту конкретную форму, я получаю эту ошибку: ошибка Uncaught: превышена максимальная глубина обновления. Это может произойти, когда компонент повторно вызывает setState внутри componentWillUpdate или componentDidUpdate. React ограничивает количество вложенных обновлений, чтобы предотвратить бесконечные циклы. я сузил ошибку до этого
useEffect(() => {
if(mode === 'create') append();
}, [append])
и это
const watchKeyOfficers = useWatch({ name: 'keyOfficers' });
// Disable selected option on next select
useEffect(() => {
if(watchKeyOfficers) {
setUsers(prevUsers => {
const newWatchKeyOfficers = watchKeyOfficers.map(ko => ko.staffName?.userid);
const newUsers = prevUsers?.map(u => ({ ...u, isDisabled: newWatchKeyOfficers.includes(u.userid) }))
return newUsers;
})
}
}, [watchKeyOfficers])
const watchBranchId = useWatch({ name: 'branchId' });
useEffect(() => {
const branchId = watchBranchId?.branchId;
setUsers(usersJson.filter(pu => pu.branchId === branchId));
if(create) {
reset({
...getValues(),
keyOfficers: [{}]
}, {
errors: true, // errors will not be reset
dirtyFields: true, // dirtyFields will not be reset
isDirty: true, // dirty will not be reset
})
}
}, [append, getValues, reset, watchBranchId])
введите код здесь
Мне интересно, есть ли лучший способ использовать useEffect, чем то, что я сделал
import React, { Fragment, useEffect, useState, useCallback } from 'react';
import { useFormContext, Controller, useFieldArray, useWatch } from "react-hook-form";
import {
Form,
Row,
Col,
Card,
Button
} from 'react-bootstrap';
import moment from 'moment';
import Select from "../../Shared/Select";
import Datepicker from "../../Shared/Datepicker";
import KeyOfficer from "../../Tooltips/KeyOfficer";
import "../../Shared/style.css";
import {Panel} from 'primereact/panel';
// Data from JSON file
import usersJson from '../../Dummy/ic4pro_users.json';
import designatesJson from '../../Dummy/ic4pro_designates.json';
import gradeJson from '../../Dummy/ic4pro_grades.json';
const years = new Array(25 1).fill().map((e,i) => {
return {label: i, value: i}
});
const months = new Array(10 1).fill().map((e,i) => {
return {label: i, value: i}
});
const StepTwo = () => {
const { register, errors, control, getValues, reset, selectedData, mode, setValue } = useFormContext();
const { fields, append, remove } = useFieldArray({
control,
name: "keyOfficers"
});
const [ users, setUsers ] = useState([...usersJson])
useEffect(() => {
if(mode === 'create') append();
}, [append])
useEffect(() => {
if(selectedData amp;amp; (mode !== 'create' || mode === null)) {
reset({
...getValues(),
keyOfficers: selectedData.keyofficers.map(sd => ({
staffName: users.find(uj => uj.userid === sd.staffName),
datejoin: moment(sd.datejoin, 'YYYYMMDD').toDate(),
jobStayYear: years.find(y => y.value === parseInt(sd.jobStayYear)),
jobStayMonth: months.find(m => m.value === parseInt(sd.jobStayMonth))
}))
})
}
else {
reset({
keyOfficers: []
})
}
}, [selectedData])
const watchKeyOfficers = useWatch({ name: 'keyOfficers' });
// Disable selected option on next select
useEffect(() => {
if(watchKeyOfficers) {
setUsers(prevUsers => {
const newWatchKeyOfficers = watchKeyOfficers.map(ko => ko.staffName?.userid);
const newUsers = prevUsers?.map(u => ({ ...u, isDisabled: newWatchKeyOfficers.includes(u.userid) }))
return newUsers;
})
}
}, [watchKeyOfficers])
const watchBranchId = useWatch({ name: 'branchId' });
useEffect(() => {
const branchId = watchBranchId?.branchId;
setUsers(usersJson.filter(pu => pu.branchId === branchId));
if(create) {
reset({
...getValues(),
keyOfficers: [{}]
}, {
errors: true, // errors will not be reset
dirtyFields: true, // dirtyFields will not be reset
isDirty: true, // dirty will not be reset
})
}
}, [append, getValues, reset, watchBranchId])
const getDesignate = useCallback((designate) => {
const designateFind = designatesJson.find(de => de.designate_id === designate)
return designateFind?.designate_name;
}, []);
const getGrade = useCallback((gradelevel) => {
const gradeFind = gradeJson.find(gr => gr.gradeID === gradelevel)
return gradeFind?.gradeType;
}, []);
const header1 = (
<>
<div >
<div>Key Officer <KeyOfficer/></div>
</div>
</>
)
return (
<Fragment>
<Panel header={header1} className="p-col-12 mb-3">
<Form.Group as={Row} >
<Form.Label column sm={3} style={{paddingLeft:"2rem"}}>
Staff Name
</Form.Label>
<Form.Label column sm={2}>
Grade Level
</Form.Label>
<Form.Label column sm={1}>
Function
</Form.Label>
<Form.Label column sm={2} className="text-center">
Length Of Stay
</Form.Label>
<Form.Label column xs={2} >
Job Stay Year
</Form.Label>
<Form.Label column sm={2} >
Job Month
</Form.Label>
</Form.Group>
{fields.map((item, index) => (
<>
<Row key={item.id}>
<Form.Group as={Col} sm="3" controlId={`keyOfficers[${index}].staffName`}>
<Controller
// id="keyofficer"
name={`keyOfficers[${index}].staffName`}
as={Select}
options={users}
// hideSelectedOptions={false}
control={control}
getOptionValue={option => option.userid}
getOptionLabel={option => `${option.title}. ${option.firstName} ${option.lastName}`}
rules={{ required: 'Staff Name is required!' }}
isInvalid={errors.keyOfficers?.[index]?.staffName}
disabled={mode === 'view' || mode === 'delete'}
defaultValue={item.staffName || ""}
/>
</Form.Group>
<Form.Group as={Col} controlId={`keyOfficers[${index}].gradeLevel`}>
<Form.Control
id="gradelevel"
name={`keyOfficers[${index}].gradeLevel`}
ref={register}
style ={{height:'1.8rem', marginTop:0, marginBottom:0}}
readOnly
defaultValue={getGrade(watchKeyOfficers?.[index]?.staffName?.gradelevel)}/>
</Form.Group>
<Form.Group as={Col} controlId={`keyOfficers[${index}].designate`}>
<Form.Control
name={`keyOfficers[${index}].designate`}
ref={register}
style ={{height:'1.8rem', marginTop:0, marginBottom:0}}
readOnly
defaultValue={getDesignate(watchKeyOfficers?.[index]?.staffName?.designate)}
/>
</Form.Group>
<Form.Group as={Col} controlId={`keyOfficers[${index}].datejoin`}>
<Controller
control={control}
name={`keyOfficers[${index}].datejoin`}
rules={{ required: 'Length of Stay is required!' }}
defaultValue={item.datejoin || ""}
render={({ onChange, onBlur, value }) => (
<Fragment >
<Datepicker
onChange={onChange}
onBlur={onBlur}
selected={value}
isInvalid={errors.keyOfficers?.[index]?.datejoin}
className="form-control is-invalid"
placeholderText="Length of Stay..."
disabled={mode === 'view' || mode === 'delete'}
/>
</Fragment>
)}
/>
</Form.Group>
<Form.Group as={Col} controlId={`keyOfficers[${index}].jobStayYear`}>
<Controller
name={`keyOfficers[${index}].jobStayYear`}
as={Select}
options={years}
control={control}
rules={{ required: 'Job Stay Year is required!' }}
isInvalid={errors.keyOfficers?.[index]?.jobStayYear}
disabled={mode === 'view' || mode === 'delete'}
defaultValue={item.jobStayYear || ""}
/>
</Form.Group>
<Form.Group as={Col} controlId={`keyOfficers[${index}].jobStayMonth`}>
<Controller
name={`keyOfficers[${index}].jobStayMonth`}
as={Select}
options={months}
control={control}
rules={{ required: 'Job Stay Month is required!' }}
isInvalid={errors.keyOfficers?.[index]?.jobStayMonth}
disabled={mode === 'view' || mode === 'delete'}
defaultValue={item.jobStayMonth || ""}
/>
</Form.Group>
{(mode === 'create' || mode === 'edit') amp;amp; (
<Form.Group as={Col} style={{marginTop:0, maxHeight:'1.8rem'}} controlId={`keyOfficers[${index}].delete`}
className="d-flex align-items-center justify-content-center" xs="auto"
>
<Button variant="danger" style={{marginTop:0, maxHeight:'1.8rem'}} size="sm" onClick={() => remove(index)}>Delete</Button>
</Form.Group>
)}
</Row>
</>
))}
{(mode === 'create' || mode === 'edit') amp;amp; (
<Form.Group>
<Button variant="primary" size="sm" style={{maxWidth:'7rem', maxHeight:'1.8rem'}} type="button" onClick={append}>Add Staff</Button>
</Form.Group>
)}
</Panel>
</Fragment>
)
}
function compare(prevProps, nextProps) {
return JSON.stringify(prevProps) === JSON.stringify(nextProps)
}
export default React.memo(StepTwo, compare);
Комментарии:
1. Это будет обновляться всякий раз, когда
append
также получает обновление, поскольку оно имеетuseEffect
зависимость. Вопрос в том, хотите ли вы, чтобыuseEffect
всегда вызывался приappend
обновлении?2. пожалуйста, создайте уменьшенное воспроизведение вашей проблемы, это трудно определить, посмотрев на компонент, который вы сумбитовали. для лучшего понимания
useEffect
вашего лучшего учебного ресурса overreacted.io/a-complete-guide-to-useeffect
Ответ №1:
useEffect(() => {
if(mode === 'create') append();
}, [append]) //// Only re-run the effect if append changes
append() выполняется в эффекте, поэтому каждый раз, когда вызывается эффект, он выполняет append() , который снова вызывает эффект, который выполняет append() … бесконечно.