在输入更改事件上使用调度会导致重新呈现整个页面 - Redux Toolkit

Using dispatch on input change event causes rerendering the whole page - Redux Toolkit

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

我正在使用 Redux 工具包。我已经对无线电输入进行了样式,当检查一个人时,它必须更改具有其值的全局状态,但在全局状态更改时不得重新启动,因为在rerender上,它会破坏样式(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 wrap your StyledRadio component. And, use 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/Whosebug/71504950/index.test.tsx
  71504950
    ✓ should pass (56 ms)

  console.log
    regType:

      at Comp (packages/redux-toolkit-example/Whosebug/71504950/index.tsx:10:11)

  console.count
    StyledRadio render: 1

      at packages/redux-toolkit-example/Whosebug/71504950/StyledRadio.tsx:4:11

  console.count
    StyledRadio render: 2

      at packages/redux-toolkit-example/Whosebug/71504950/StyledRadio.tsx:4:11

  console.log
    regType:  private

      at Comp (packages/redux-toolkit-example/Whosebug/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

触发radio change事件后,通过CHANGE_REG_TYPE 动作改变stateComp会re-render,此时StyledRadio的props和之前render一样,所以React.memo会跳过re-render组件.

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

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