在不使用setState的情况下响应本机状态更改



我有一个屏幕,它表示一个时间表,让用户为每个星期命名。一旦用户完成所有周的编辑,他将按下复选标记,应用程序应更新后端。

我遇到了一个非常奇怪的错误,第一次单击复选标记编辑数据时会更新(日志显示"editData changed",ui也会更改(,但当我在updateTheDB中打印状态时,它还没有更新。如果我尝试再次进入编辑模式并在不进行任何新更改的情况下保存,则先前的更改将在updateTheDB中更新。

我以为这是一个引用复制,而不是价值问题,但我使用的是JSON.parse(JSON.stringify来复制,所以这不可能。

对setState(setEditData(的调用位于onSavePressed内部,该调用处于模式中,当用户尝试命名一周时打开。

有人知道是什么原因造成的吗?

编辑

当setEditToServer被调用时,我希望引起渲染

这是我的代码:

const Schedule = (props: IPorps) => {
const { week_names_props, navigation } = props;
const days = ['s', 'm', 't', 'w', 't', 's', 'w'];
//bottom Modal
const [active_modal, setActiveModal] = useState<MProps>(null)
//data 
const [serverData, setServerData] = useState<IData>({ weekNames: week_names_props, weekEvents: {} })
//edit_mode data
const [editData, setEditData] = useState<IData>(null)
//other
const [isLoading, setIsLoading] = useState<boolean>(false)
const [editable, setEditable] = useState<boolean>(false)
const doTheLoadingThingy = (): void => {
Axios.get(`api/weeks/getWeekNameByCompanyId`, {
params: {
company_id: 1,
},
}).then((response) => {
setServerData({ ...serverData, weekNames: response.data })
//useEffect will hide the loading and the modal
})
.catch(() => { setIsLoading(false); errorToast("get") })
}
const setEditToServer = () => {
console.log("setEditToServer"
setEditData({
weekNames: JSON.parse(JSON.stringify(serverData.weekNames)),
weekEvents: {}
})
}}
useEffect(() => {
setEditToServer()
setIsLoading(false)
}, [serverData])

useEffect(() => {
console.log("editData changed")
}, [editData])
const updateTheDB = () => {
setIsLoading(true)
console.log(editData)
//send to backend
}
useEffect(() => {
navigation.setOptions({
headerLeft: () => (
<View style={{ flexDirection: "row", justifyContent: "space-around", alignItems: "center", paddingHorizontal: 30 }}>
{editable ? (
<>
<Icon
name={"check"}
onPress={() => {
updateTheDB()
setEditable(false)
}}/>
<Icon
name={"cancel"}
onPress={() => {
console.log("cancel")
setEditToServer()
setEditable(false)
}}/>
</>
) : (
<Icon
name={'edit'}
onPress={() => setEditable(true)}/>
)}
</View>
)
})
}, [editable])
return (
<>
{isLoading ?
(<ActivityIndicator size="large" />) :
(
<>
<BottomModal
innerComponent={active_modal ? active_modal.modal : null}
innerComponentProps={active_modal ? active_modal.props : null}
modalStyle={active_modal ? active_modal.style : null}
modalVisible={(active_modal != null)}
onClose={() => {
setActiveModal(null);
}}
/>
<WeekDaysHeader />
{editData ? 
(<FlatList
data={editData.weekNames}
keyExtractor={(item) => item.week.toString()}
style={styles.flatListStyle}
// extraData={editData?editData.weekEvents:null}
renderItem={({ item }) => {
return (
<Week
days={days}
events={editData.weekEvents[item.week.toString()]}
dayComponents={ScheduleScreenDay}
week_name={item.week_name ? item.week_name : null}
week_number={Number(item.week)}
onHeaderPressed={editable ?
(week_number, week_title) => {
console.log("Pressed", week_number, week_title)
setActiveModal({
props: {
week_number_props: week_number,
week_title_props: week_title,
onSavePressedProps: (new_name) => {
if (new_name) {
let tmp = JSON.parse(JSON.stringify(editData.weekNames))
const i = tmp.findIndex((item) => item.week.toString() === week_number.toString())
tmp[i].week_name = new_name
setEditData((prev) => ({ weekNames: tmp, weekEvents: prev.weekEvents }))
}
}
}, modal: NameWeekModalComponet,
style: styles.weekNameModalStyle
});
} : undefined}
/>
);
}}
/>) : null}
</>
)
}
</>)
};
export default Schedule;

我以为这是一个引用复制,而不是值问题,但我正在使用JSON.parse(JSON.stringfy(进行复制,所以这不可能。

这正是问题所在:

// Always true
JSON.parse(JSON.stringify(['a'])) !== JSON.parse(JSON.stringify(['a']))

因此,每当调用setEditToServer时,组件都会重新渲染,这是因为React在决定渲染时会与以前的状态进行浅层比较。

const setEditToServer = () => {
// Always re-render
setEditData({
weekNames: JSON.parse(JSON.stringify(serverData.weekNames)),
weekEvents: {}
})
}}

问题就在这个用途中效果:

useEffect(() => {
navigation.setOptions({
headerLeft: () => (
//...
)
})
}, [editable])

它里面的函数使用了editData状态,但useEffect在deps列表中没有它,所以当单击onPress时,它得到了editData的旧状态。解决方案是将editData添加到deps列表中。像这样:

useEffect(() => {
navigation.setOptions({
headerLeft: () => (
//...
)
})
}, [editable, editData])

最新更新