首页 > 解决方案 > 在没有 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;

标签: reactjsreact-nativereact-hooks

解决方案


我认为这是通过引用而不是价值问题的副本,但我正在使用 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: {}
        })
    }}

推荐阅读