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-state,它是一个布尔值。我使用 useSelector() 将状态导入组件内部,并使组件订阅该状态的状态更改。
在 return 语句中,我使用 {} 注入一个三元运算符,该运算符呈现按钮(如果游戏尚未开始,也就是 gameStarted-state 等于 false)并呈现 ,hello" 文本并让按钮如果用户单击按钮启动 SingleQuestionsMode(又名 gameStarted 已设置为 true),则消失。
但是如果我点击开始按钮,我可以在浏览器的 devConsole 中看到问题被正确获取,并且通过 redux-devtools 我可以看到 gameStarted redux 状态被正确设置为 true(从 false最初),但组件仍然没有重新呈现(不显示“你好”占位符并且按钮不消失)。
来自浏览器开发控制台的屏幕截图:
单击按钮之前:
before button is clicked
点击按钮后:
After Button has been clicked
这是为什么?即使我最初将 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
我正在开发测验游戏,我希望 SingleChoice 组件从 QuizApi 中获取 SingleChoice 问题。 当用户单击开始按钮以播放 singleQuestionMode 时,组件应获取问题并将其显示在屏幕上(出于测试目的,我只是显示了 ,hello" 文本)。然后开始按钮应该消失(单击后)。
为了实现这一点,我创建了一个名为 gameStarted 的 redux-state,它是一个布尔值。我使用 useSelector() 将状态导入组件内部,并使组件订阅该状态的状态更改。 在 return 语句中,我使用 {} 注入一个三元运算符,该运算符呈现按钮(如果游戏尚未开始,也就是 gameStarted-state 等于 false)并呈现 ,hello" 文本并让按钮如果用户单击按钮启动 SingleQuestionsMode(又名 gameStarted 已设置为 true),则消失。
但是如果我点击开始按钮,我可以在浏览器的 devConsole 中看到问题被正确获取,并且通过 redux-devtools 我可以看到 gameStarted redux 状态被正确设置为 true(从 false最初),但组件仍然没有重新呈现(不显示“你好”占位符并且按钮不消失)。
来自浏览器开发控制台的屏幕截图: 单击按钮之前: before button is clicked
点击按钮后: After Button has been clicked
这是为什么?即使我最初将 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