reactjs - 在没有 setState 的情况下响应本机状态更改
问题描述
我有一个屏幕,它代表一个时间表,让用户每周给一个名字。一旦用户完成所有星期的编辑,他将按下复选标记并且应用程序应该更新后端。
我遇到了一个非常奇怪的错误,第一次单击复选标记时,编辑数据会更新(日志显示“editData 已更改”并且 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.stringify) 进行复制,所以这不可能。
这正是问题所在:
// 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: {}
})
}}
推荐阅读
- css - 如何使用 css 实现像素完美的像素艺术升级?
- python - 扩展pydatatable中的数据框时如何将f表达式列传递给函数?
- node.js - 如何根据许可证安装仅适用于一个项目的私有 NPM 包?
- c - 我什么时候应该使用动态内存分配?
- pega - 如何使用 pega 系统变量检查 SLA 规则是否已执行?
- c# - Mqtt client.connect 运行时失败
- android - Flutter Flat Button Color Property Not Working
- c++ - 如何使用 CMake 在 android 构建上编译和共享两个 c++ 库
- javascript - 开始时标签是否可能导致css和js无法加载
- amazon-cloudformation - 在 AWS 实例创建之间添加延迟