簡體   English   中英

ReduxToolkit useSelector Hook:通過 useDispatch 更新選定的 redux-state 后,React 功能組件不會重新渲染

[英]ReduxToolkit useSelector Hook: React functional component doesnt rerender after selected redux-state is updated through useDispatch

我正在開發一個測驗游戲,我希望 SingleChoice 組件從 QuizApi 獲取 SingleChoice 問題。 當用戶單擊開始按鈕時,要播放 singleQuestionMode,組件應該獲取問題並將它們顯示在屏幕上(出於測試目的,我只是顯示了一個 ,,hello" 文本)。然后開始按鈕應該消失(點擊后)。

為了實現這一點,我創建了一個名為 gameStarted 的 redux 狀態,它是一個布爾值。 我使用 useSelector() 來導入組件內部的狀態,並為組件訂閱該狀態的狀態更改。 在 return 語句中,我使用 {} 注入了一個三元運算符,該運算符呈現按鈕(如果游戲尚未開始,也就是 gameStarted-state 等於 false),它呈現 ,,hello" 文本並讓按鈕在用戶消失時消失單擊按鈕以啟動 SingleQuestionsMode(又名 gameStarted 已設置為 true)。

但是,如果我單擊開始按鈕,我可以在瀏覽器的 devConsole 中看到,問題被正確獲取,並且通過 redux-devtools 我可以看到 gameStarted redux 狀態正確設置為 true(最初為 false),但仍然組件不重新呈現(不顯示“hello”-占位符並且按鈕不消失)。

瀏覽器中開發控制台的屏幕截圖:單擊按鈕之前:單擊按鈕之前

單擊按鈕后:單擊按鈕后

這是為什么? 即使我最初將 gameStarted redux-state 設置為 true,它也會顯示 ,,hello"- 占位符文本而不是按鈕。所以這一切似乎都設置正確,但在 gameStarted redux 狀態更改后,有些東西會阻止重新渲染。也許我也使用過 redux-persist?

以下是所有相關代碼:

SingleChoice.js 代碼:

import React, { useEffect, useState} from 'react';
import {useSelector, useDispatch} from 'react-redux';
import {selectGameStatus, setGameStarted} from "../loginSlice";

export default function SingleChoice() {
    const dispatch = useDispatch();
    const [questions, setQuestions] = useState([]);

    const gameStarted = useSelector(selectGameStatus);
    
    const fetchSingleQuestions = async () => {
        const questionData = await fetch('url-here');
        const questions = await questionData.json();
        setQuestions(questions.results)
        console.log(questions.results);
    }

    const startGame = () => {
        fetchSingleQuestions();
        dispatch(setGameStarted());
    }
     

    return (
        <div>
            <h1>SingleChoiceMode</h1>
            {!gameStarted ? <button onClick={startGame}>Spiel starten</button> : <div><h1>Hello</h1></div>}
        </div>
    )
}

具有上述 gameStarted 狀態的切片代碼:

import { createAsyncThunk, createSlice } from '@reduxjs/toolkit';


const initialState = {
    loggedIn: false,
    accountInfo: {
        id: "",
        username: "",
        mail: "", 
        password: "",
        singlescore: "", 
        multiscore: "", 
        mixedscore: ""
    },
    gameStarted: false
};
export const LoginSlice = createSlice({

    name: 'login',
    initialState,
    reducers: {
        setLoginTrue: (state) => {
            state.loggedIn = true;
        },
        setLoginFalse: (state) => {
            state.loggedIn = false;
        },
        setAccountInfo: (state, action) => {
            state.accountInfo = {
                id: action.payload.id,
                username: action.payload.username,
                mail: action.payload.mail,
                password: action.payload.password,
                singlescore: action.payload.singlescore,
                multiscore: action.payload.multiscore,
                mixedscore: action.payload.mixedscore
            }
        },
        setGameStarted: (state) => {
            state.gameStarted = true;
        },
        setGameStopped: (state) => {
            state.gameStarted = false;
        }
    }
});

export const selectLoginState = (state) => state.login.loggedIn;
export const selectAccountInfo = (state) => state.login.accountInfo;
export const selectGameStatus = (state) => state.gameStarted;
export const { setLoginTrue, setLoginFalse, setAccountInfo, setGameStarted, setGameStopped } = LoginSlice.actions;
export default LoginSlice.reducer;

redux-store 的代碼(我也使用 redux-persist 來保持用戶登錄):

import { configureStore } from '@reduxjs/toolkit';
import loginReducer from '../features/loginSlice';
import storage from "redux-persist/lib/storage";
import {combineReducers} from "redux"; 
import { persistReducer } from 'redux-persist'

const reducers = combineReducers({
  login: loginReducer
})

const persistConfig = {
  key: 'root',
  storage
};

const persistedReducer = persistReducer(persistConfig, reducers);


const store = configureStore({
  reducer: persistedReducer,
  devTools: process.env.NODE_ENV !== 'production'
});

export default store;

Index.js 的代碼:

ReactDOM.render(
  <React.StrictMode>
    <BrowserRouter>
      <Provider store={store}>
        <PersistGate loading={null} persistor={persistor}>
          <App />
        </PersistGate>
      </Provider>
    </BrowserRouter>
  </React.StrictMode>,
  document.getElementById('root')
);

路由和渲染所有內容的組件 App.js 的代碼

import React from 'react';
import Home from "./features/Home";
import SingleChoice from "./features/modes/SingleChoice"
import MultipleChoice from "./features/modes/MultipleChoice"
import Mixed from "./features/modes/Mixed"
import Login from "./features/Login"
import Profile from "./features/Profile"
import Rankings from "./features/Rankings"
import NotFound from "./features/NotFound"
import Register from "./features/Register"
import Protected from "./features/Protected"
import { NavBar } from "./features/Navbar";
import './App.css';
import { Routes, Route } from "react-router-dom";

function App() {

  return (
    <div className="App">
      <NavBar />
      <Routes>
        <Route path="/" element={<Home />} />
        <Route element={<Protected />}>
          <Route path="/single" element={<SingleChoice />} />
          <Route path="/multiple" element={<MultipleChoice />} />
          <Route path="/mixed" element={<Mixed />} />
          <Route path="/profile" element={<Profile />} />
          <Route path="/rankings" element={<Rankings />} />
        </Route>
        <Route path="/login" element={<Login />} />
        <Route path="/register" element={<Register />} />
        <Route path="*" element={<NotFound />} />
      </Routes>
    </div>
  );
}

export default App;

請幫助我,以便 SingleChoice 組件在導入的 redux-state 發生更改后最終重新渲染。

SingleChoice.js中,而不是

const gameStarted = useSelector(selectGameStatus);

    ...

const startGame = () => {
    fetchSingleQuestions();
    dispatch(setGameStarted());
}

類型 :

const { gameStarted } = useSelector((state) => state.login);

...

const startGame = () => {
    fetchSingleQuestions();
    dispatch(setGameStarted(true));
}

loginSlice.js 中替換

setGameStarted: (state) => {
    state.gameStarted = true;
},

經過 :

setGameStarted: (state, action) => {
    state.gameStarted = action.payload;
},

演示: Stackblitz

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM