在输入更改事件上使用dispatch会导致重新绘制整个页面-Redux Toolkit



我是React和Redux的新手,遇到了这个问题:

我正在使用Redux Toolkit。我设计了无线电输入的样式,当选中一个时,它必须用它的值更改全局状态,但当全局状态更改时,不能重新渲染,因为在重新渲染时,它会破坏样式(CSS:checked选择器工作不正常(。只有使用该状态的元素才能更改,例如按钮。当全局状态发生变化时,如何防止它们重新绘制?我做错了什么?提前谢谢。

const dispatch = useDispatch()
const { regType } = useSelector((state) => state.regTypes)
const handleChange = (e) => {
dispatch(changeRegType(e.target.value))
}
return (
<form>
<StyledRadio name="registrationType" id="private" value="private" onChange={handleChange} >
<StyledRadio name="registrationType" id="company" value="company" onChange={handleChange} />
<Button type="submit" disabled={!!regType ? true : false}>Next</Button>
</form>
)

切片

export const registrationTypesSlice = createSlice({
name: "registrationType",
initialState: {
regType: "",
},
reducers: {
changeRegType: (state, action) => {
state.regType = action.payload
},
},
})
export const { changeRegType } = registrationTypesSlice.actions
export default registrationTypesSlice.reducer

简单回答:您需要使用React.memo-HOC包装StyledRadio组件。并且,使用useCallback钩子创建handleChange事件处理程序的记忆版本。

长答案,见以下代码:

index.tsx:

import React, { useCallback } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { StyledRadio } from './StyledRadio';
const changeRegType = (v) => ({ type: 'CHANGE_REG_TYPE', payload: v });
export function Comp() {
const dispatch = useDispatch();
const { regType } = useSelector((state: any) => state.regTypes);
console.log('regType: ', regType);
const handleChange = useCallback(
(e) => {
const { value } = e.target;
dispatch(changeRegType(value));
},
[dispatch],
);
return (
<form>
<StyledRadio name="registrationType" data-testid="private" id="private" value="private" onChange={handleChange} />
<StyledRadio name="registrationType" data-testid="company" id="company" value="company" onChange={handleChange} />
<button type="submit" disabled={!!regType ? true : false}>
Next
</button>
</form>
);
}

StyledRadio.tsx:

import React from 'react';
export const StyledRadio = React.memo((props: React.ComponentPropsWithoutRef<'input'>) => {
console.count('StyledRadio render');
return <input {...props} type="radio" />;
});

index.test.tsx:

import { configureStore } from '@reduxjs/toolkit';
import { fireEvent, render, screen } from '@testing-library/react';
import React from 'react';
import { Provider } from 'react-redux';
import { Comp } from './';
const store = configureStore({
reducer: (state = { regTypes: { regType: '' } }, action) => {
switch (action.type) {
case 'CHANGE_REG_TYPE':
return { ...state, regTypes: { regType: action.payload } };
default:
return state;
}
},
});
describe('71504950', () => {
test('should pass', () => {
render(
<Provider store={store}>
<Comp />
</Provider>,
);
fireEvent.click(screen.getByTestId('private'), { target: { value: 'private' } });
});
});

测试结果:

PASS   redux-toolkit-example  packages/redux-toolkit-example/stackoverflow/71504950/index.test.tsx
71504950
✓ should pass (56 ms)
console.log
regType:
at Comp (packages/redux-toolkit-example/stackoverflow/71504950/index.tsx:10:11)
console.count
StyledRadio render: 1
at packages/redux-toolkit-example/stackoverflow/71504950/StyledRadio.tsx:4:11
console.count
StyledRadio render: 2
at packages/redux-toolkit-example/stackoverflow/71504950/StyledRadio.tsx:4:11
console.log
regType:  private
at Comp (packages/redux-toolkit-example/stackoverflow/71504950/index.tsx:10:11)
Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        4.303 s

正如您所看到的,当Comp组件首次呈现日志时:

// first render
regType:
StyledRadio render: 1
StyledRadio render: 2
// second render
regType:  private

触发无线电更改事件后,通过CHANGE_REG_TYPE动作更改stateComp会重新渲染,此时StyledRadio的道具与之前的渲染相同,因此React.memo会跳过重新渲染组件。

如果您删除useCallbackReact.memo,您将获得日志:

// first render
regType:
StyledRadio render: 1
StyledRadio render: 2
// second render
regType:  private
StyledRadio render: 3
StyledRadio render: 4

最新更新