辅助函数在反应中覆盖映射列表中的先前值
helper function overwriting previous values in mapped list in react
我有一个申请人的映射列表,每个都包含一个 finalStatus 属性。此映射列表呈现带有自定义下拉列表的组件,并将申请人作为道具。
我有一个包含下拉对象的数据文件,我将其拉入组件以呈现下拉列表。
其中每一个都有一个禁用的 属性 true 或 false,最初设置为 false,就像这样...
complianceStatus: [
{id: 1, value: 'None', disabled: false },
{id: 2, value: 'Provisionally', disabled: false},
{id: 3, value: 'Fully', disabled: false },
],
然后当组件挂载时 运行 一个 useEffect 来获取下拉数据并对其执行辅助功能,以便根据申请人的值确定禁用应该是真还是假目的。然后我将此对象设置为 state 以呈现下拉列表。
这是使用效果
useEffect(() => {
setDropdownData(utils.getDropdownData('complianceStatus', applicant));
}, [applicant]);
我的 util 函数在这里供参考
export const getDropdownData = (type, applicant) => {
const optionData = mergeArray(data.dropdownData[type], type, applicant);
return optionData;
}
const mergeArray = (dropDownData, type, data) => {
if (type === 'actionList') {
mergeArrayActionListHelper(dropDownData, data);
}
if (type === 'complianceStatus') {
disableComplianceStatus(dropDownData, data);
}
return dropDownData;
}
export const disableComplianceStatus = (dropdownData, applicant) => {
switch(applicant.finalStatus) {
case 'fully_compliant':
dropdownData[0].disabled = true;
dropdownData[1].disabled = true;
dropdownData[2].disabled = true;
return;
case 'partially_compliant':
dropdownData[0].disabled = true;
dropdownData[1].disabled = true;
dropdownData[2].disabled = false;
return;
default:
dropdownData[0].disabled = true;
dropdownData[1].disabled = false;
dropdownData[2].disabled = false;
return;
}
}
我的下拉列表正在呈现,但有一个奇怪的怪癖。
问题是,列表中的最后一位申请人似乎覆盖了之前所有申请人的残疾属性。
知道为什么会这样吗?
编辑有关申请人的更多信息
申请者是一个大物件几个组件起来。它适用于 table 风格的仪表板,每个申请人都有一行。
它首先传递给未设置状态的 DashboardRow 组件。
return (
<div className='dashboard-row'>
{
staticDash ? (
<DashboardRowStatic applicant={applicant}/>
) : (
<DashboardRowDynamic
applicant={applicant}
/>
)
}
</div>
)
这会在行上呈现实际项目,组件看起来像这样
const DashboardRowDynamic = ({applicant, applicantNumber, applicantLength}) => {
const dynamicTableData = useSelector(state => state.dashboard.dynamicTableData);
return (
dynamicTableData && dynamicTableData.length > 0 &&
<div className='dashboard-row-dynamic'>
{
dynamicTableData[1].values.map(value => {
if (value === 'finalStatus') {
return (
<div key={value} className='col-md center'>
<ProfileFinalStatus
applicantFromDash={applicant}
applicantNumber={applicantNumber}
applicantLength={applicantLength}
/>
</div>
)
}
else if (value === 'suspendedMessage') {
return (
<div key={value} className='col-md'>
<TooltipLg title={applicant[value]}>
<p className='orka-semi-p tooltip'>{applicant[value]}</p>
</TooltipLg>
</div>
)
} else {
return (
<div key={value} className='col-md'>
<p className='orka-semi-p'>{applicant[value]}</p>
</div>
)
}
})
}
</div>
)
}
然后这是整个 FinalStatus 组件供参考
const ProfileFinalStatus = ({applicantFromDash, applicantNumber = 0, applicantLength = 20}) => {
const applicant = useSelector(state => state.profile.applicant);
const dispatch = useDispatch();
const [dropdownData, setDropdownData] = useState([]);
const [defaultData, setDefaultData] = useState(null);
const [localApplicant, setLocalApplicant] = useState(null);
useEffect(() => {
const applicantSelect = applicantFromDash ? applicantFromDash : applicant;
setLocalApplicant(applicantSelect);
setDropdownData(utils.getDropdownData('complianceStatus', applicantSelect));
}, [applicant, applicantFromDash]);
useEffect(() => {
if (localApplicant) {
const data = dropdownData.filter(item => {
return item.dbValue === localApplicant.finalStatus;
});
getIcon(data);
setDefaultData(data[0]);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [JSON.stringify(dropdownData), JSON.stringify(localApplicant)]);
const getIcon = (data) => {
switch(data[0].dbValue) {
case 'partially_compliant':
data[0].icon = partially;
return;
case 'fully_compliant':
data[0].icon = fully;
return;
default:
data[0].icon = none;
return;
}
}
const getDropdownVal = (val) => {
const payload = {
finalStatus: val.dbValue
}
apiApplicant.updateApplicant(localApplicant.workerUuid, payload)
.then(res => {
const data = dropdownData.filter(item => {
return item.dbValue === payload.finalStatus;
});
localApplicant.finalStatus = val.dbValue;
setDropdownData(utils.getDropdownData('complianceStatus', localApplicant));
setDefaultData(data[0]);
if (applicantFromDash) {
dispatch(refetchApplicants());
}
dispatch(setAlert({
type: 'success',
message: 'Successfully updated compliance status',
isVisible: true
}));
})
.catch(err => {
dispatch(setAlert({
type: 'error',
message: 'Error updating face to face status',
isVisible: true
}));
})
}
const getPosition = () => {
let position;
// applicantLength = 2;
// if (applicantLength === 2) {
// position = 'top-center';
// } else {
position = applicantNumber > 16 ? 'top-center' : 'bottom-center';
// }
// console.log(applicantLength)
// console.log(position)
return position;
}
return (
<div>
{
defaultData &&
(
<Dropdown
type="complianceStatus"
dropdownData={dropdownData}
defaultData={defaultData}
getDropdownVal={getDropdownVal}
width="160"
height='24'
mobileResponsive={false}
position={getPosition()}
/>
)
}
</div>
)
}
export default ProfileFinalStatus;
编辑 2
dropdownData 在文件 data.js
中
export const dropdownData = {
searchBy: [
{id: 1, value: 'First Name', dbValue: 'name', class: "dropdown-grey"},
{id: 2, value: 'Surname', dbValue: 'surname', class: "dropdown-grey"},
{id: 3, value: 'Email', dbValue: 'email', class: "dropdown-grey"},
{id: 4, value: 'NI Number', dbValue: 'nationalInsuranceNumber', class: "dropdown-grey"}
],
sortBy: [
{id: 1, value: 'Urgent', dbValue: 'urgent', sortAsc: false, class: "dropdown-grey"},
{id: 2, value: 'Name', dbValue: 'name', sortAsc: false, class: "dropdown-grey"},
{id: 3, value: 'Email', dbValue: 'email', sortAsc: false, class: "dropdown-grey"},
{id: 4, value: 'Created Date', dbValue: 'created_at', sortAsc: false, class: "dropdown-grey"}
],
submissionStatus: [
{id: 1, value: 'Pending', dbValue: 'pending', class: "dropdown-grey"},
{id: 2, value: 'Accepted', dbValue: 'accepted', class: "dropdown-grey"},
{id: 3, value: 'Rejected', dbValue: 'rejected', class: "dropdown-grey"},
{id: 4, value: 'Unsuccessful', dbValue: 'unsuccessful', class: "dropdown-grey"}
],
complianceStatus: [
{id: 1, value: 'None', dbValue: 'not_compliant', class: "dropdown-grey", disabled: false },
{id: 2, value: 'Provisionally', dbValue: 'partially_compliant', class: "dropdown-yellow", disabled: false},
{id: 3, value: 'Fully', dbValue: 'fully_compliant', class: "dropdown-green", disabled: false },
],
actionList: [
{id: 1, value: 'Suspended', openModal: true, isSuspended: false },
{id: 2, value: 'Mark Urgent', openModal: false, isUrgent: false },
{id: 3, value: 'Edit User', openModal: true },
{id: 4, value: 'CV Online', openModal: false },
{id: 5, value: 'Identity Check', openModal: true },
],
employmentTypes: [
{id: 1, dbValue: 'employed', value: 'Employed', fullView: true, class: 'blue-outline' },
{id: 2, dbValue: 'self-employed', value: 'Self Employed', fullView: false, class: 'blue-outline' },
{id: 3, dbValue: 'studying', value: 'Studying', fullView: true, class: 'blue-outline' },
{id: 4, dbValue: 'carer', value: 'Carer', fullView: false, class: 'blue-outline' },
{id: 5, dbValue: 'redundant', value: 'Redundant', fullView: true, class: 'blue-outline' },
{id: 6, dbValue: 'sick', value: 'Sick', fullView: false, class: 'blue-outline' },
{id: 7, dbValue: 'travelling', value: 'Travelling', fullView: false, class: 'blue-outline' },
{id: 8, dbValue: 'volunteering', value: 'Volunteering', fullView: true, class: 'blue-outline' },
{id: 9, dbValue: 'unemployed-claiming', value: 'Unemployed Claiming', fullView: false, class: 'blue-outline' },
{id: 10, dbValue: 'unemployed-not-claiming', value: 'Unemployed Not Claiming', fullView: false, class: 'blue-outline' },
],
faceToFaceActions: [
{id: 1, value: 'Pending', dbValue: 'pending', class: "dropdown-grey"},
{id: 2, value: 'Approve', dbValue: 'passed', class: "dropdown-grey"},
{id: 3, value: 'Reject', dbValue: 'failed', class: "dropdown-grey"}
]
}
然后有一个导入数据的 utils 函数文件
import * as data from './data.js';
可以看到上面的utils函数
然后在最终状态组件 utils 是这样导入的
import * as utils from '../../utils/utilsFunctions';
然后在 ProfileFinalStatus 组件中调用它,如上面的代码片段所示。
问题
我认为问题是 disableComplianceStatus
实用程序中的 dropdownData
对象突变。
我认为发生的步骤是:
DashboardRowDynamic
呈现 table 数据数组并呈现 ProfileFinalStatus
组件。
ProfileFinalStatus
通过调用 setDropdownData(utils.getDropdownData('complianceStatus', applicantSelect));
. 填充其 dropdownData
状态数组
getDropdownData
将 data.dropdownData[type]
作为 dropdownData
传递给 mergeArray
和 returns mergeArray
的 return 值。
mergeArray
调用 disableComplianceStatus
并传递 dropdownData
和 returns dropdownData
.
disableComplianceStatus
直接改变 dropdownData
对象,设置 disabled
属性.
当这发生在循环中时,每次迭代都会改变同一个对象,因为它仍然是对存储在从 data.js
文件导出的对象中的原始对象的引用。每个迭代项都会改变引用的对象,并且正在改变所有先前元素具有的对象引用。
解决方案
在此流程中的某个时刻,您只需要浅拷贝 dropdownData
对象,以便每个映射元素都获得自己的副本进行变异。这个 IMO 的一个好地方是 disableComplianceStatus
实用程序。与其改变传递的对象,不如复制、设置属性,然后 return 新对象。 mergeArray
应该 return that 新对象而不是传递的 dropdownData
对象。
我向您提交以下更改
mergeArray
return它调用的实用程序的结果。
const mergeArray = (dropDownData, type, data) => {
if (type === 'actionList') {
return mergeArrayActionListHelper(dropDownData, data);
}
if (type === 'complianceStatus') {
return disableComplianceStatus(dropDownData, data);
}
return dropDownData;
}
disableComplianceStatus
浅拷贝 dropdownData
对象 然后 更新属性,returning 新对象引用。这些需要在您更新其属性的每个级别完成,因此这也包括数组元素。
注意:您需要对 mergeArrayActionListHelper
实用程序应用类似的修复程序。
export const disableComplianceStatus = (dropdownData, applicant) => {
let disabled = [];
switch(applicant.finalStatus) {
case 'fully_compliant':
disabled = [true, true, true];
break;
case 'partially_compliant':
disabled = [true, true, false];
break;
default:
disabled = [true, false, false];
break;
}
// return new mapped array with copies of all elements.
return dropdownData.map((el, i) => ({
...el,
disabled: !!disabled[i],
}));
}
我有一个申请人的映射列表,每个都包含一个 finalStatus 属性。此映射列表呈现带有自定义下拉列表的组件,并将申请人作为道具。
我有一个包含下拉对象的数据文件,我将其拉入组件以呈现下拉列表。
其中每一个都有一个禁用的 属性 true 或 false,最初设置为 false,就像这样...
complianceStatus: [
{id: 1, value: 'None', disabled: false },
{id: 2, value: 'Provisionally', disabled: false},
{id: 3, value: 'Fully', disabled: false },
],
然后当组件挂载时 运行 一个 useEffect 来获取下拉数据并对其执行辅助功能,以便根据申请人的值确定禁用应该是真还是假目的。然后我将此对象设置为 state 以呈现下拉列表。
这是使用效果
useEffect(() => {
setDropdownData(utils.getDropdownData('complianceStatus', applicant));
}, [applicant]);
我的 util 函数在这里供参考
export const getDropdownData = (type, applicant) => {
const optionData = mergeArray(data.dropdownData[type], type, applicant);
return optionData;
}
const mergeArray = (dropDownData, type, data) => {
if (type === 'actionList') {
mergeArrayActionListHelper(dropDownData, data);
}
if (type === 'complianceStatus') {
disableComplianceStatus(dropDownData, data);
}
return dropDownData;
}
export const disableComplianceStatus = (dropdownData, applicant) => {
switch(applicant.finalStatus) {
case 'fully_compliant':
dropdownData[0].disabled = true;
dropdownData[1].disabled = true;
dropdownData[2].disabled = true;
return;
case 'partially_compliant':
dropdownData[0].disabled = true;
dropdownData[1].disabled = true;
dropdownData[2].disabled = false;
return;
default:
dropdownData[0].disabled = true;
dropdownData[1].disabled = false;
dropdownData[2].disabled = false;
return;
}
}
我的下拉列表正在呈现,但有一个奇怪的怪癖。
问题是,列表中的最后一位申请人似乎覆盖了之前所有申请人的残疾属性。
知道为什么会这样吗?
编辑有关申请人的更多信息
申请者是一个大物件几个组件起来。它适用于 table 风格的仪表板,每个申请人都有一行。
它首先传递给未设置状态的 DashboardRow 组件。
return (
<div className='dashboard-row'>
{
staticDash ? (
<DashboardRowStatic applicant={applicant}/>
) : (
<DashboardRowDynamic
applicant={applicant}
/>
)
}
</div>
)
这会在行上呈现实际项目,组件看起来像这样
const DashboardRowDynamic = ({applicant, applicantNumber, applicantLength}) => {
const dynamicTableData = useSelector(state => state.dashboard.dynamicTableData);
return (
dynamicTableData && dynamicTableData.length > 0 &&
<div className='dashboard-row-dynamic'>
{
dynamicTableData[1].values.map(value => {
if (value === 'finalStatus') {
return (
<div key={value} className='col-md center'>
<ProfileFinalStatus
applicantFromDash={applicant}
applicantNumber={applicantNumber}
applicantLength={applicantLength}
/>
</div>
)
}
else if (value === 'suspendedMessage') {
return (
<div key={value} className='col-md'>
<TooltipLg title={applicant[value]}>
<p className='orka-semi-p tooltip'>{applicant[value]}</p>
</TooltipLg>
</div>
)
} else {
return (
<div key={value} className='col-md'>
<p className='orka-semi-p'>{applicant[value]}</p>
</div>
)
}
})
}
</div>
)
}
然后这是整个 FinalStatus 组件供参考
const ProfileFinalStatus = ({applicantFromDash, applicantNumber = 0, applicantLength = 20}) => {
const applicant = useSelector(state => state.profile.applicant);
const dispatch = useDispatch();
const [dropdownData, setDropdownData] = useState([]);
const [defaultData, setDefaultData] = useState(null);
const [localApplicant, setLocalApplicant] = useState(null);
useEffect(() => {
const applicantSelect = applicantFromDash ? applicantFromDash : applicant;
setLocalApplicant(applicantSelect);
setDropdownData(utils.getDropdownData('complianceStatus', applicantSelect));
}, [applicant, applicantFromDash]);
useEffect(() => {
if (localApplicant) {
const data = dropdownData.filter(item => {
return item.dbValue === localApplicant.finalStatus;
});
getIcon(data);
setDefaultData(data[0]);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [JSON.stringify(dropdownData), JSON.stringify(localApplicant)]);
const getIcon = (data) => {
switch(data[0].dbValue) {
case 'partially_compliant':
data[0].icon = partially;
return;
case 'fully_compliant':
data[0].icon = fully;
return;
default:
data[0].icon = none;
return;
}
}
const getDropdownVal = (val) => {
const payload = {
finalStatus: val.dbValue
}
apiApplicant.updateApplicant(localApplicant.workerUuid, payload)
.then(res => {
const data = dropdownData.filter(item => {
return item.dbValue === payload.finalStatus;
});
localApplicant.finalStatus = val.dbValue;
setDropdownData(utils.getDropdownData('complianceStatus', localApplicant));
setDefaultData(data[0]);
if (applicantFromDash) {
dispatch(refetchApplicants());
}
dispatch(setAlert({
type: 'success',
message: 'Successfully updated compliance status',
isVisible: true
}));
})
.catch(err => {
dispatch(setAlert({
type: 'error',
message: 'Error updating face to face status',
isVisible: true
}));
})
}
const getPosition = () => {
let position;
// applicantLength = 2;
// if (applicantLength === 2) {
// position = 'top-center';
// } else {
position = applicantNumber > 16 ? 'top-center' : 'bottom-center';
// }
// console.log(applicantLength)
// console.log(position)
return position;
}
return (
<div>
{
defaultData &&
(
<Dropdown
type="complianceStatus"
dropdownData={dropdownData}
defaultData={defaultData}
getDropdownVal={getDropdownVal}
width="160"
height='24'
mobileResponsive={false}
position={getPosition()}
/>
)
}
</div>
)
}
export default ProfileFinalStatus;
编辑 2 dropdownData 在文件 data.js
中export const dropdownData = {
searchBy: [
{id: 1, value: 'First Name', dbValue: 'name', class: "dropdown-grey"},
{id: 2, value: 'Surname', dbValue: 'surname', class: "dropdown-grey"},
{id: 3, value: 'Email', dbValue: 'email', class: "dropdown-grey"},
{id: 4, value: 'NI Number', dbValue: 'nationalInsuranceNumber', class: "dropdown-grey"}
],
sortBy: [
{id: 1, value: 'Urgent', dbValue: 'urgent', sortAsc: false, class: "dropdown-grey"},
{id: 2, value: 'Name', dbValue: 'name', sortAsc: false, class: "dropdown-grey"},
{id: 3, value: 'Email', dbValue: 'email', sortAsc: false, class: "dropdown-grey"},
{id: 4, value: 'Created Date', dbValue: 'created_at', sortAsc: false, class: "dropdown-grey"}
],
submissionStatus: [
{id: 1, value: 'Pending', dbValue: 'pending', class: "dropdown-grey"},
{id: 2, value: 'Accepted', dbValue: 'accepted', class: "dropdown-grey"},
{id: 3, value: 'Rejected', dbValue: 'rejected', class: "dropdown-grey"},
{id: 4, value: 'Unsuccessful', dbValue: 'unsuccessful', class: "dropdown-grey"}
],
complianceStatus: [
{id: 1, value: 'None', dbValue: 'not_compliant', class: "dropdown-grey", disabled: false },
{id: 2, value: 'Provisionally', dbValue: 'partially_compliant', class: "dropdown-yellow", disabled: false},
{id: 3, value: 'Fully', dbValue: 'fully_compliant', class: "dropdown-green", disabled: false },
],
actionList: [
{id: 1, value: 'Suspended', openModal: true, isSuspended: false },
{id: 2, value: 'Mark Urgent', openModal: false, isUrgent: false },
{id: 3, value: 'Edit User', openModal: true },
{id: 4, value: 'CV Online', openModal: false },
{id: 5, value: 'Identity Check', openModal: true },
],
employmentTypes: [
{id: 1, dbValue: 'employed', value: 'Employed', fullView: true, class: 'blue-outline' },
{id: 2, dbValue: 'self-employed', value: 'Self Employed', fullView: false, class: 'blue-outline' },
{id: 3, dbValue: 'studying', value: 'Studying', fullView: true, class: 'blue-outline' },
{id: 4, dbValue: 'carer', value: 'Carer', fullView: false, class: 'blue-outline' },
{id: 5, dbValue: 'redundant', value: 'Redundant', fullView: true, class: 'blue-outline' },
{id: 6, dbValue: 'sick', value: 'Sick', fullView: false, class: 'blue-outline' },
{id: 7, dbValue: 'travelling', value: 'Travelling', fullView: false, class: 'blue-outline' },
{id: 8, dbValue: 'volunteering', value: 'Volunteering', fullView: true, class: 'blue-outline' },
{id: 9, dbValue: 'unemployed-claiming', value: 'Unemployed Claiming', fullView: false, class: 'blue-outline' },
{id: 10, dbValue: 'unemployed-not-claiming', value: 'Unemployed Not Claiming', fullView: false, class: 'blue-outline' },
],
faceToFaceActions: [
{id: 1, value: 'Pending', dbValue: 'pending', class: "dropdown-grey"},
{id: 2, value: 'Approve', dbValue: 'passed', class: "dropdown-grey"},
{id: 3, value: 'Reject', dbValue: 'failed', class: "dropdown-grey"}
]
}
然后有一个导入数据的 utils 函数文件
import * as data from './data.js';
可以看到上面的utils函数
然后在最终状态组件 utils 是这样导入的
import * as utils from '../../utils/utilsFunctions';
然后在 ProfileFinalStatus 组件中调用它,如上面的代码片段所示。
问题
我认为问题是 disableComplianceStatus
实用程序中的 dropdownData
对象突变。
我认为发生的步骤是:
DashboardRowDynamic
呈现 table 数据数组并呈现ProfileFinalStatus
组件。ProfileFinalStatus
通过调用setDropdownData(utils.getDropdownData('complianceStatus', applicantSelect));
. 填充其 getDropdownData
将data.dropdownData[type]
作为dropdownData
传递给mergeArray
和 returnsmergeArray
的 return 值。mergeArray
调用disableComplianceStatus
并传递dropdownData
和 returnsdropdownData
.disableComplianceStatus
直接改变dropdownData
对象,设置disabled
属性.
dropdownData
状态数组
当这发生在循环中时,每次迭代都会改变同一个对象,因为它仍然是对存储在从 data.js
文件导出的对象中的原始对象的引用。每个迭代项都会改变引用的对象,并且正在改变所有先前元素具有的对象引用。
解决方案
在此流程中的某个时刻,您只需要浅拷贝 dropdownData
对象,以便每个映射元素都获得自己的副本进行变异。这个 IMO 的一个好地方是 disableComplianceStatus
实用程序。与其改变传递的对象,不如复制、设置属性,然后 return 新对象。 mergeArray
应该 return that 新对象而不是传递的 dropdownData
对象。
我向您提交以下更改
mergeArray
return它调用的实用程序的结果。const mergeArray = (dropDownData, type, data) => { if (type === 'actionList') { return mergeArrayActionListHelper(dropDownData, data); } if (type === 'complianceStatus') { return disableComplianceStatus(dropDownData, data); } return dropDownData; }
disableComplianceStatus
浅拷贝dropdownData
对象 然后 更新属性,returning 新对象引用。这些需要在您更新其属性的每个级别完成,因此这也包括数组元素。
注意:您需要对 mergeArrayActionListHelper
实用程序应用类似的修复程序。
export const disableComplianceStatus = (dropdownData, applicant) => {
let disabled = [];
switch(applicant.finalStatus) {
case 'fully_compliant':
disabled = [true, true, true];
break;
case 'partially_compliant':
disabled = [true, true, false];
break;
default:
disabled = [true, false, false];
break;
}
// return new mapped array with copies of all elements.
return dropdownData.map((el, i) => ({
...el,
disabled: !!disabled[i],
}));
}