React dom 未使用 Redux 存储进行更新
React dom not updating with Redux store
我是 react-redux 的新手,我在开发一个网络应用程序时遇到了问题。该应用程序应该具有用户登录功能,它应该能够从我创建的数据库 api 中获取并显示游戏列表,并且它应该能够在以下时间显示该列表中特定游戏的信息它被点击了。
我的用户登录功能运行良好,但游戏列表和特定游戏详细信息最初并未显示在浏览器中。如果我查看 redux devtools,动作被调度并且 returns 状态的正确信息,如果我翻阅 devtools(按下播放按钮),列表将显示在 dom,并停留直到我刷新页面。游戏细节也是如此。
我不确定哪里出了问题。我尝试调整我正在使用的 React 组件和容器,但我在其他 post 中想到/发现的任何东西似乎都不起作用。也许这是我如何设置初始状态的问题(我在用户登录缩减器和游戏缩减器中都有一个初始状态)?
我将 post 我认为是此 post 中的相关代码块。
store/reducers/currentUser.js
import { SET_CURRENT_USER } from "../actionTypes";
const DEFAULT_STATE = {
isAuthenticated: false, //hopefully be true, when user is logged in
user: {} //all user info when logged in
};
export default (state = DEFAULT_STATE, action) => {
switch (action.type) {
case SET_CURRENT_USER:
return {
// turn empty object into false, or if there are keys true
isAuthenticated: !!Object.keys(action.user).length,
user: action.user
};
default:
return state;
}
};
stor/reducers/games.js
import { LOAD_GAMES, SET_CURRENT_GAME } from "../actionTypes";
const initState = {
current: {},
list: []
}
const game = (state = initState, action) => {
switch (action.type) {
case LOAD_GAMES:
state.list = action.games;
return state
case SET_CURRENT_GAME:
state.current = action.game;
return state;
default:
return state;
}
};
export default game;
store/reducers/index.js(根reducer文件)
import {combineReducers} from "redux";
import currentUser from "./currentUser";
import games from "./games";
import errors from "./errors";
const rootReducer = combineReducers({
currentUser,
games,
errors
});
export default rootReducer;
store/index.js(店铺组成文件)
import rootReducer from "./reducers";
import { createStore, applyMiddleware, compose } from "redux";
import thunk from "redux-thunk";
export function configureStore() {
const store = createStore(
rootReducer,
compose(
applyMiddleware(thunk),
window.__REDUX_DEVTOOLS_EXTENSION__ ? window.__REDUX_DEVTOOLS_EXTENSION__() : f => f
)
);
return store;
}
store/actions/games.js
import { apiCall } from "../../services/api";
import { addError } from "./errors";
import { LOAD_GAMES, SET_CURRENT_GAME } from "../actionTypes";
export const loadGames = games => ({
type: LOAD_GAMES,
games
});
export const setCurrentGame = game => ({
type: SET_CURRENT_GAME,
game
});
export const fetchGames = () => {
return dispatch => {
return apiCall("GET", "api/games/")
.then(res => {
dispatch(loadGames(res));
})
.catch(err => {
dispatch(addError(err.message));
});
};
};
//WRITE A FUNCTION TO SET_CURRENT_GAME TO BE THE ID OF THE GAME THAT IS CLICKED ON.
export const getGameDetails = game_id => {
return dispatch => {
return apiCall("GET", `/api/games/${game_id}`)
.then(res => {
dispatch(setCurrentGame(res));
})
.catch(err => {
dispatch(addError(err.message));
});
};
};
export const postNewGame = title => (dispatch, getState) => {
return apiCall("post", "/api/games", { title })
.then(res => {})
.catch(err => addError(err.message));
};
React 容器和组件:
App.js
import React from 'react';
import {Provider} from "react-redux";
import {configureStore} from "../store";
import {BrowserRouter as Router} from "react-router-dom";
import Navbar from "./Navbar";
import Main from "./Main";
import {setAuthorizationToken, setCurrentUser} from "../store/actions/auth";
import jwtDecode from "jwt-decode";
const store = configureStore();
if (localStorage.jwtToken) {
setAuthorizationToken(localStorage.jwtToken);
// prevent someone from manually tampering with the key of jwtToken in localStorage
try {
store.dispatch(setCurrentUser(jwtDecode(localStorage.jwtToken)));
} catch (e) {
store.dispatch(setCurrentUser({}));
}
}
const App = () => (
<Provider store={store}>
<Router>
<div className="onboarding">
<Navbar />
<Main />
</div>
</Router>
</Provider>
);
export default App;
Main.js(包含具有 Gamelist 容器的 Hompage 组件)
import React from "react";
import {Switch, Route, withRouter, Redirect} from "react-router-dom";
import {connect} from "react-redux";
import Homepage from "../components/Homepage";
import AuthForm from "../components/AuthForm";
import {authUser} from "../store/actions/auth";
import {removeError} from "../store/actions/errors"
import withAuth from "../hocs/withAuth";
import GameForm from "./GameForm";
import GamePage from "../components/GamePage";
const Main = props => {
const {authUser, errors, removeError, currentUser} = props;
return (
<div className="container">
<Switch>
<Route path="/" exact render={props => <Homepage currentUser={currentUser} {...props} /> } />
<Route
path="/signin" exact
render={props => {
return(
<AuthForm
removeError={removeError}
errors={errors}
onAuth={authUser}
buttonText="Log in"
heading="Welcome Back."
{...props}
/>
)
}} />
<Route
path="/signup" exact
render={props => {
return(
<AuthForm
removeError={removeError}
errors={errors}
onAuth={authUser}
signUp
buttonText="Sign me up"
heading="Join Weekly Matchup today."
{...props}
/>
)
}}
/>
<Route
path="/games/new" exact
component={withAuth(GameForm)}
/>
<Route
path="/games/:game_id"
render={props => {
return(
<GamePage
currentUser={currentUser}
{...props}
/>
)
}}
/>
<Redirect to="/" />
</Switch>
</div>
)
}
function mapStateToProps(state){
return {
currentUser: state.currentUser,
errors: state.errors
};
}
export default withRouter(connect(mapStateToProps, {authUser, removeError})(Main));
Homepage.js(显示GameList容器的组件)
import React from "react";
import { Link } from "react-router-dom";
import GameList from "../containers/GameList";
const Homepage = ({ currentUser }) => {
if (!currentUser.isAuthenticated) {
return (
<div className="home-hero">
<h1>Welcome to the Weekly Matchup!</h1>
<h4>Weekly Matchup is a web app that allows you to vote for which characters you think are favored to win in a one-on-one matchup in a variety of fighting games.</h4>
<p>If you would like to vote for and comment on this week's matchups, please be sure to make an account by clicking the link below, or sign in!</p>
<Link to="/signup" className="btn btn-primary">
Sign up here
</Link>
</div>
);
}
return (
<div>
<div className="home-hero">
<h4>Click on the games below to see this week's matchups.</h4>
<GameList />
</div>
</div>
);
};
export default Homepage;
GameList.js(从商店调用 fetchGames 动作生成游戏列表的容器
import React, { Component } from "react";
import { connect } from "react-redux";
import { Link } from "react-router-dom";
import { fetchGames } from "../store/actions/games";
class GameList extends Component {
componentDidMount() {
this.props.fetchGames();
}
render() {
const { list } = this.props;
let gameList = list.map(g => (
<li className="list-group-item" key= {g._id}>
<Link to={`/games/${g._id}`}>
{g.title}
</Link>
</li>
));
return (
<div className="row col-sm-8">
<div className="offset-1 col-sm-10">
<ul className="list-group" id="games">
{gameList}
</ul>
</div>
</div>
);
}
}
function mapStateToProps(state) {
return {
list: state.games.list
};
}
export default connect(mapStateToProps, { fetchGames })(
GameList
);
我会到此为止,因为这是第一次出现问题。我知道我已经 post 编辑了很多代码,但我不确定在这种情况下什么是相关的或不相关的。
以这种方式更新状态是个好习惯,不要直接改变
我发现两个 payload action.games
和 action.game
是故意的还是拼写错误?
import { LOAD_GAMES, SET_CURRENT_GAME } from "../actionTypes";
const initState = {
current: {},
list: []
}
const game = (state = initState, action) => {
switch (action.type) {
case LOAD_GAMES:
return {...state,
list:action.game
}
case SET_CURRENT_GAME:
return {...state,
current:action.games,
}
default:
return state;
}
};
export default game;
我是 react-redux 的新手,我在开发一个网络应用程序时遇到了问题。该应用程序应该具有用户登录功能,它应该能够从我创建的数据库 api 中获取并显示游戏列表,并且它应该能够在以下时间显示该列表中特定游戏的信息它被点击了。
我的用户登录功能运行良好,但游戏列表和特定游戏详细信息最初并未显示在浏览器中。如果我查看 redux devtools,动作被调度并且 returns 状态的正确信息,如果我翻阅 devtools(按下播放按钮),列表将显示在 dom,并停留直到我刷新页面。游戏细节也是如此。
我不确定哪里出了问题。我尝试调整我正在使用的 React 组件和容器,但我在其他 post 中想到/发现的任何东西似乎都不起作用。也许这是我如何设置初始状态的问题(我在用户登录缩减器和游戏缩减器中都有一个初始状态)?
我将 post 我认为是此 post 中的相关代码块。
store/reducers/currentUser.js
import { SET_CURRENT_USER } from "../actionTypes";
const DEFAULT_STATE = {
isAuthenticated: false, //hopefully be true, when user is logged in
user: {} //all user info when logged in
};
export default (state = DEFAULT_STATE, action) => {
switch (action.type) {
case SET_CURRENT_USER:
return {
// turn empty object into false, or if there are keys true
isAuthenticated: !!Object.keys(action.user).length,
user: action.user
};
default:
return state;
}
};
stor/reducers/games.js
import { LOAD_GAMES, SET_CURRENT_GAME } from "../actionTypes";
const initState = {
current: {},
list: []
}
const game = (state = initState, action) => {
switch (action.type) {
case LOAD_GAMES:
state.list = action.games;
return state
case SET_CURRENT_GAME:
state.current = action.game;
return state;
default:
return state;
}
};
export default game;
store/reducers/index.js(根reducer文件)
import {combineReducers} from "redux";
import currentUser from "./currentUser";
import games from "./games";
import errors from "./errors";
const rootReducer = combineReducers({
currentUser,
games,
errors
});
export default rootReducer;
store/index.js(店铺组成文件)
import rootReducer from "./reducers";
import { createStore, applyMiddleware, compose } from "redux";
import thunk from "redux-thunk";
export function configureStore() {
const store = createStore(
rootReducer,
compose(
applyMiddleware(thunk),
window.__REDUX_DEVTOOLS_EXTENSION__ ? window.__REDUX_DEVTOOLS_EXTENSION__() : f => f
)
);
return store;
}
store/actions/games.js
import { apiCall } from "../../services/api";
import { addError } from "./errors";
import { LOAD_GAMES, SET_CURRENT_GAME } from "../actionTypes";
export const loadGames = games => ({
type: LOAD_GAMES,
games
});
export const setCurrentGame = game => ({
type: SET_CURRENT_GAME,
game
});
export const fetchGames = () => {
return dispatch => {
return apiCall("GET", "api/games/")
.then(res => {
dispatch(loadGames(res));
})
.catch(err => {
dispatch(addError(err.message));
});
};
};
//WRITE A FUNCTION TO SET_CURRENT_GAME TO BE THE ID OF THE GAME THAT IS CLICKED ON.
export const getGameDetails = game_id => {
return dispatch => {
return apiCall("GET", `/api/games/${game_id}`)
.then(res => {
dispatch(setCurrentGame(res));
})
.catch(err => {
dispatch(addError(err.message));
});
};
};
export const postNewGame = title => (dispatch, getState) => {
return apiCall("post", "/api/games", { title })
.then(res => {})
.catch(err => addError(err.message));
};
React 容器和组件: App.js
import React from 'react';
import {Provider} from "react-redux";
import {configureStore} from "../store";
import {BrowserRouter as Router} from "react-router-dom";
import Navbar from "./Navbar";
import Main from "./Main";
import {setAuthorizationToken, setCurrentUser} from "../store/actions/auth";
import jwtDecode from "jwt-decode";
const store = configureStore();
if (localStorage.jwtToken) {
setAuthorizationToken(localStorage.jwtToken);
// prevent someone from manually tampering with the key of jwtToken in localStorage
try {
store.dispatch(setCurrentUser(jwtDecode(localStorage.jwtToken)));
} catch (e) {
store.dispatch(setCurrentUser({}));
}
}
const App = () => (
<Provider store={store}>
<Router>
<div className="onboarding">
<Navbar />
<Main />
</div>
</Router>
</Provider>
);
export default App;
Main.js(包含具有 Gamelist 容器的 Hompage 组件)
import React from "react";
import {Switch, Route, withRouter, Redirect} from "react-router-dom";
import {connect} from "react-redux";
import Homepage from "../components/Homepage";
import AuthForm from "../components/AuthForm";
import {authUser} from "../store/actions/auth";
import {removeError} from "../store/actions/errors"
import withAuth from "../hocs/withAuth";
import GameForm from "./GameForm";
import GamePage from "../components/GamePage";
const Main = props => {
const {authUser, errors, removeError, currentUser} = props;
return (
<div className="container">
<Switch>
<Route path="/" exact render={props => <Homepage currentUser={currentUser} {...props} /> } />
<Route
path="/signin" exact
render={props => {
return(
<AuthForm
removeError={removeError}
errors={errors}
onAuth={authUser}
buttonText="Log in"
heading="Welcome Back."
{...props}
/>
)
}} />
<Route
path="/signup" exact
render={props => {
return(
<AuthForm
removeError={removeError}
errors={errors}
onAuth={authUser}
signUp
buttonText="Sign me up"
heading="Join Weekly Matchup today."
{...props}
/>
)
}}
/>
<Route
path="/games/new" exact
component={withAuth(GameForm)}
/>
<Route
path="/games/:game_id"
render={props => {
return(
<GamePage
currentUser={currentUser}
{...props}
/>
)
}}
/>
<Redirect to="/" />
</Switch>
</div>
)
}
function mapStateToProps(state){
return {
currentUser: state.currentUser,
errors: state.errors
};
}
export default withRouter(connect(mapStateToProps, {authUser, removeError})(Main));
Homepage.js(显示GameList容器的组件)
import React from "react";
import { Link } from "react-router-dom";
import GameList from "../containers/GameList";
const Homepage = ({ currentUser }) => {
if (!currentUser.isAuthenticated) {
return (
<div className="home-hero">
<h1>Welcome to the Weekly Matchup!</h1>
<h4>Weekly Matchup is a web app that allows you to vote for which characters you think are favored to win in a one-on-one matchup in a variety of fighting games.</h4>
<p>If you would like to vote for and comment on this week's matchups, please be sure to make an account by clicking the link below, or sign in!</p>
<Link to="/signup" className="btn btn-primary">
Sign up here
</Link>
</div>
);
}
return (
<div>
<div className="home-hero">
<h4>Click on the games below to see this week's matchups.</h4>
<GameList />
</div>
</div>
);
};
export default Homepage;
GameList.js(从商店调用 fetchGames 动作生成游戏列表的容器
import React, { Component } from "react";
import { connect } from "react-redux";
import { Link } from "react-router-dom";
import { fetchGames } from "../store/actions/games";
class GameList extends Component {
componentDidMount() {
this.props.fetchGames();
}
render() {
const { list } = this.props;
let gameList = list.map(g => (
<li className="list-group-item" key= {g._id}>
<Link to={`/games/${g._id}`}>
{g.title}
</Link>
</li>
));
return (
<div className="row col-sm-8">
<div className="offset-1 col-sm-10">
<ul className="list-group" id="games">
{gameList}
</ul>
</div>
</div>
);
}
}
function mapStateToProps(state) {
return {
list: state.games.list
};
}
export default connect(mapStateToProps, { fetchGames })(
GameList
);
我会到此为止,因为这是第一次出现问题。我知道我已经 post 编辑了很多代码,但我不确定在这种情况下什么是相关的或不相关的。
以这种方式更新状态是个好习惯,不要直接改变
我发现两个 payload action.games
和 action.game
是故意的还是拼写错误?
import { LOAD_GAMES, SET_CURRENT_GAME } from "../actionTypes";
const initState = {
current: {},
list: []
}
const game = (state = initState, action) => {
switch (action.type) {
case LOAD_GAMES:
return {...state,
list:action.game
}
case SET_CURRENT_GAME:
return {...state,
current:action.games,
}
default:
return state;
}
};
export default game;