React中实现按钮点击无刷新切换状态的解决方案
问题描述
我有一组“Yes”“No”按钮,点击后必须刷新页面才会改变颜色。目前用临时方案:在handleClick中添加refresh(),强制每次点击按钮时刷新整个页面。虽然能改变按钮颜色,但希望点击时页面不刷新、不回到顶部,实现按钮颜色无缝切换。
尝试过用e.preventDefault()阻止页面刷新,但因未向处理函数传递事件,不确定当前场景的使用方式。
相关代码如下:
<React.Fragment> <Button style={{ margin: '0px 5px' }} variant="contained" onClick={() => handleClick(row.insight_id, row.device_id, 1) } color={ row.userConfirmed == null ? 'default' : row.userConfirmed === 1 ? 'primary' : 'default' } > Yes </Button> <Button style={{ margin: '0px 5px' }} variant="contained" onClick={() => handleClick(row.insight_id, row.device_id, 0) } color={ row.userConfirmed == null ? 'default' : row.userConfirmed === 1 ? 'default' : 'primary' } > No </Button> </React.Fragment>
const handleClick = (insightId, deviceId, value) => { dispatch(updateUserConfirm(insightId, deviceId, value)); refresh(); };
补充代码:
const userInsightsReducer = (state = initialState, action) => { switch (action.type) { . . . case UPDATE_USER_CONFIRM_BEGIN: return { ...state, result: 'BEGINNING', }; case UPDATE_USER_CONFIRM_SUCCESS: const userInsightsDataNew = state.userInsightsData const insightIndex = state.userInsightsData.findIndex(insight => insight.device_id === action.payload.deviceId && insight.insight_id === action.payload.insightId ) const updatedInsight = {...state.userInsightsData[insightIndex], userConfirmed:action.payload.value} userInsightsDataNew[insightIndex] = updatedInsight return { ...state, userInsightsData : userInsightsDataNew, result: "SUCCESS", }; case UPDATE_USER_CONFIRM_FAILURE: return { ...state, result: 'FAILURE',
let row = userInsights.userInsightsData[index];
解决方案
1. 移除强制刷新逻辑
直接删掉handleClick里的refresh()调用——页面刷新完全是手动触发的,只要去掉这行代码,就不会再强制刷新页面。Redux状态更新后会自动触发组件重渲染,根本不需要刷新页面来同步UI。
修改后的handleClick:
const handleClick = (insightId, deviceId, value) => { dispatch(updateUserConfirm(insightId, deviceId, value)); };
2. 修复Reducer的状态不可变问题
你的Reducer里直接修改了原数组userInsightsDataNew[insightIndex] = updatedInsight,这违反了Redux状态不可变的原则,会导致Redux检测不到状态变化,组件无法自动重渲染。
需要创建新数组来更新状态:
case UPDATE_USER_CONFIRM_SUCCESS: // 对原数组做浅拷贝,生成新数组 const userInsightsDataNew = [...state.userInsightsData]; const insightIndex = state.userInsightsData.findIndex(insight => insight.device_id === action.payload.deviceId && insight.insight_id === action.payload.insightId ); if (insightIndex !== -1) { // 创建新的insight对象替换数组对应位置 userInsightsDataNew[insightIndex] = { ...state.userInsightsData[insightIndex], userConfirmed: action.payload.value }; } return { ...state, userInsightsData: userInsightsDataNew, result: "SUCCESS", };
通过[...state.userInsightsData]创建新数组,确保状态引用发生变化,Redux就能感知到更新,触发组件重渲染,按钮颜色自然会同步变化。
3. (可选)简化按钮颜色判断逻辑
可以把颜色判断抽成独立函数,让代码更易读:
const getButtonColor = (userConfirmed, targetValue) => { return userConfirmed === null ? 'default' : (userConfirmed === targetValue ? 'primary' : 'default'); }; // 按钮使用示例: <Button style={{ margin: '0px 5px' }} variant="contained" onClick={() => handleClick(row.insight_id, row.device_id, 1)} color={getButtonColor(row.userConfirmed, 1)} > Yes </Button> <Button style={{ margin: '0px 5px' }} variant="contained" onClick={() => handleClick(row.insight_id, row.device_id, 0)} color={getButtonColor(row.userConfirmed, 0)} > No </Button>
关于e.preventDefault()的说明
你的场景里页面刷新并非由浏览器默认行为(比如表单提交、链接跳转)触发,而是手动调用refresh()导致的,所以e.preventDefault()完全不适用,去掉refresh()才是根本解决办法。
内容的提问来源于stack exchange,提问作者Henrik
相关产品推荐
相关产品推荐

