You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Next.js页面刷新后开关按钮状态无法持久化问题求助

问题

在Next.js开发的配置页面中,开关按钮切换后状态可从active变为maintenance,点击保存会弹出成功提示,但刷新页面后开关会恢复为active状态。需要实现刷新页面后开关保持修改后的状态,切换回原状态时刷新也能同步对应状态。

接口返回的单条配置JSON结构:

{
  "id": "d9cbf36e-bb51-11ee-a740-00155db75f50",
  "name": "Beranda",
  "code": "home",
  "position": 1,
  "is_maintenance": false
}
解决方案

核心问题分析

当前代码的问题在于:

  1. checkStatus初始化时依赖空数组config,导致初始状态为空对象,接口返回数据后未同步更新checkStatus
  2. 保存成功后未重新拉取最新配置,虽然刷新会重新请求,但页面状态无法立即与后端同步

修改后的完整代码

import React, { useEffect, useState } from 'react'
import { useRouter } from 'next/router'
import { WebConfigurationType, WebConfigType } from '@/type/web'
import { getData, postData, putData } from '@/utility/fetch'
import { showMessage } from '@/components/alerts/showMessage';

const STATUS_COLORS = {
    active: 'green',
    maintenance: 'red',
};

const STATUS_TEXT = {
    active: 'Aktif',
    maintenance: '(Maintenance)',
};

const CheckboxSwitch = ({ label, name, checked, onChange }: any) => {
    const labelColor = checked ? STATUS_COLORS.maintenance : STATUS_COLORS.active;

    return (
        <div className="input-banner mb-5">
            <div className="mb-2 flex justify-between gap-4">
                <div className="flex gap-2">
                    <div className="label-banner style={black}">{label}</div>
                    <div className="label-banner" style={{ color: labelColor }}>
                        {checked ? STATUS_TEXT.maintenance : STATUS_TEXT.active}
                    </div>
                </div>

                <label className="relative h-6 w-12">
                    <input
                        type="checkbox"
                        className="custom_switch peer absolute z-10 h-full w-full cursor-pointer opacity-0"
                        id={`custom_switch_checkbox_${name}`}
                        checked={checked}
                        onChange={onChange}
                        name={name}
                    />
                    <span className={`block h-full rounded-full bg-[#ebedf2] before:absolute before:bottom-1 before:left-1 before:h-4 before:w-4 before:rounded-full before:bg-white before:transition-all before:duration-300 peer-checked:bg-primary peer-checked:before:left-7 dark:bg-dark dark:before:bg-white-dark dark:peer-checked:before:bg-white`}></span>
                </label>
            </div>
        </div>
    );
};

function WebConfig() {

    const router = useRouter()
    const [webConfig, setWebConfig] = useState<WebConfigType>()
    const [config, setConfig] = useState<WebConfigurationType[]>([])
    const [checkStatus, setCheckStatus] = useState<Record<string, boolean>>({})

    // 统一生成checkStatus的工具函数
    const generateCheckStatus = (data: WebConfigurationType[]) => {
        return data.reduce((acc, item) => {
            acc[item.name] = item.is_maintenance
            return acc
        }, {} as Record<string, boolean>)
    }

    useEffect(() => {
        const getDataWeb = async () => {
            try {
                const {data} = await getData('/page-configuration')
                setConfig(data.page_configuration)
                // 接口返回数据后同步更新checkStatus
                setCheckStatus(generateCheckStatus(data.page_configuration))
            } catch (error: any) {
                console.error('获取配置失败:', error)
            }
        }
        getDataWeb()
    },[])


    const handleChangeBox = (name: string) => {
        setCheckStatus(prev => ({...prev, [name]: !prev[name]}))
    }

    const generateDialog = async () => {
        try {
            await putData('/page-configuration', checkStatus)
            showMessage('Berhasil', 'success')
            // 保存成功后重新拉取最新配置,确保状态完全同步
            const {data} = await getData('/page-configuration')
            setConfig(data.page_configuration)
            setCheckStatus(generateCheckStatus(data.page_configuration))
        } catch (error) {
            showMessage('Gagal', 'error')
            console.error('保存配置失败:', error)
        }
    }

    const handleMultiplyCheck = async (ev: React.FormEvent) => {
        ev.preventDefault()
        generateDialog()
    }

    return (
        <>
            <div className='h-full w-full p-4'>
                <div className='w-full h-full bg-white rounded-lg shadow-md border border-slate-200 p-3'>
                    <div className='w-full px-4 py-3 flex justify-between items-center mb-9'>
                        <div>
                            <h3 className='font-semibold text-3xl'>Web Configuration</h3>
                        </div>
                    </div>
                    <div className='w-full p-3 flex flex-col gap-4'>
                        {config.map((item, index) => (
                            <CheckboxSwitch 
                                key={item.id || index} // 添加唯一key,符合React列表渲染规范
                                label={item.name} 
                                checked={checkStatus[item.name] ?? false} // 兜底处理,避免undefined
                                onChange={() => handleChangeBox(item.name)}/>
                        ))}
                        <button 
                            onClick={handleMultiplyCheck}
                            className='p-2 rounded-md bg-blue-400 text-white font-semibold'
                        >Simpan</button>
                    </div>
                </div>
            </div>
        </>
    )
}

export default WebConfig

关键修改点说明

  1. 同步初始状态:在接口获取到配置数据后,调用setCheckStatus更新状态,确保初始值与后端数据完全一致
  2. 保存后刷新数据:保存成功后重新拉取最新配置,让页面状态立即同步后端,无需等待刷新
  3. 添加兜底处理:在checked属性中使用?? false,避免checkStatus中无对应key时出现undefined导致的渲染错误
  4. 规范列表渲染:给CheckboxSwitch添加唯一key,符合React性能优化要求

内容的提问来源于stack exchange,提问作者Hovi Sohibul Wafa

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 15:47:11