尽管Redux状态发生了变化,但useSelector钩子在React组件中没有更新

aurhwmvo  于 2023-04-21  发布在  React
关注(0)|答案(1)|浏览(179)

我在React中创建了一个打字游戏,使用Redux配合Toolkit来管理状态,在DesktopSlice中,我记录了某些面板是打开还是关闭,在Console组件中,我使用useSelector钩子从桌面slice中获取面板状态,问题是useSelector总是返回面板对象的初始状态。我有一个运行命令的Console组件,还有一个显示面板的PanelsWrapper**组件。

  • main.tsx
  • App.tsx
  • Console.tsx
  • PanelsWrapper.tsx

PanelsWrapper中面板显示正常,但在我的Console.tsx中状态始终为初始状态。
下面是我的DesktopSlice代码:

import { createSlice } from "@reduxjs/toolkit";
import type { PayloadAction } from "@reduxjs/toolkit";
import { PANELS } from '../../data/panels';

type VisiblePanels = {[key in PANELS]?: boolean} 

export type DesktopState = {
    panels: VisiblePanels
}

const initialState: DesktopState = {
    panels: {
        [PANELS.FRIDGE]: false,
        [PANELS.WORKTOP]: false,
        [PANELS.RECIPEBOOK]: false,
    }
};

export const desktopSlice = createSlice({
    name: "desktop",
    initialState,
    reducers: {
        toggleOpenPanel: (state, action: PayloadAction<{panelType: PANELS, opened?: boolean}>) => {
            state.panels[action.payload.panelType] = action.payload.opened;
        },
    },
});

export const { toggleOpenPanel} = desktopSlice.actions;
export default desktopSlice.reducer;

下面是我的Console.tsx组件代码:

const Console : FC = () => {
  const inputRef = useRef<HTMLInputElement>(null);
  const [consoleLog,setConsoleLog] = useState<ICommandLine[]>([]);

  const openedPanels = useSelector((state: RootState) => state.desktop.panels);
  const dispatch = useDispatch();

  const focusConsole = () => {
    inputRef.current && inputRef.current.focus();
  }

  const enterCommand = () => {
    if(inputRef.current && inputRef.current.value){
        //get command value
        const command = inputRef.current.value;
        console.log(openedPanels);
        
        //interpret the command and get a response
        const res : commandResponse | undefined = runCommandInterpreter(command);
        if(res){
            const cmdLine : ICommandLine = {
                command: command,
                info: res.message,
                infoType: res.code
            }
            setConsoleLog(oldArray => [...oldArray,cmdLine]);
        }

        //clear the input
        inputRef.current.value="";
    } 
  }

  const runCommandInterpreter = (input:string) : commandResponse | undefined => {
    const command_args = input.trim().split(" ");
    const command = command_args[0];
    const command_parameters = command_args.slice(1);

    switch(command){
        case COMMAND.OPEN:
        case COMMAND.CLOSE:
            const panelName = command_parameters.join(" ");
            const panel = getPanelByName(panelName);
            const opened = command === COMMAND.OPEN;
            if(panel){
                dispatch(toggleOpenPanel({panelType: panel, opened: opened}));
                return sendResponse(COMMAND_CODE.OK);
            } else {
                return sendResponse(
                    COMMAND_CODE.ERROR, 
                    `${panelName} is not a valid PANEL name.`
                );
            }
    }
  }

  const handleKeyDown = (e:KeyboardEvent) => {
    switch(e.key){
        case 'Enter':
            enterCommand();
            break;
    }
  }

  useEffect(() => {
    inputRef.current?.addEventListener("keydown",handleKeyDown);
  },[]);

  return (
    <StyledConsole onClick={focusConsole}>
        <StyledConsoleHistory>
            {
                consoleLog.map((command,index) => (
                    <CommandLine 
                        key={`${index}-${command.command}`} 
                        command={command.command}
                        info={command.info}
                        infoType={command.infoType}
                    />
                ))
            }
        </StyledConsoleHistory>
        <StyledConsoleLabel>
            <StyledConsoleCommand>
                What would you like to do?
            </StyledConsoleCommand>
            <StyledConsoleInput type="text" ref={inputRef} />
        </StyledConsoleLabel>
    </StyledConsole>
  )
}

export default Console

问题是openedPanels总是返回desktopSlice.panels的初始状态,即使Redux商店中的状态正确更新也不会更新。这个问题不会发生在其他也使用useSelector的组件中,如PanelsWrapper。
有人能帮助我了解可能导致此问题的原因吗?

EDIT我修复了这个问题。问题是我在我的组件上使用了addEventListener而不是onKeyDown,组件永远不会有更新的状态。

4si2a6ki

4si2a6ki1#

你能试试const {panels} = useSelector((state: RootState) => state.desktop)吗?我想你应该解构一下

相关问题