React dom不使用Redux存储更新 [英] React dom not updating with Redux store

查看:80
本文介绍了React dom不使用Redux存储更新的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是React-redux的新手,我正在开发的Web应用程序有问题.该应用程序应该具有用户登录功能,它应该能够从我制作的数据库api中获取并显示游戏列表,并且应该能够在单击该列表时显示该列表中特定游戏的信息./p>

我的用户登录功能运行良好,但是游戏列表和特定游戏详细信息最初并未显示在浏览器中.如果我查看redux devtools,则会分派该动作并将正确的信息返回给状态,如果我翻阅devtools(按一下播放按钮),则列表将显示在dom中,并一直停留到刷新为止这一页.游戏细节也是如此.

我不确定这是怎么回事.我试图调整我正在使用的react组件和容器,但是我能想到的/在其他帖子中找不到的似乎没有用.也许这与我如何设置初始状态有关(在用户登录还原器和游戏还原器中都具有初始状态)?

我将在这篇文章中发布我认为是相关的代码块.

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(减速器根文件)

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));
};

反应容器和组件: 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
);

我会停在那里,因为这是第一次出现问题.我知道我已经发布了很多代码,但是我不确定在这种情况下什么是相关的或不相关的.

解决方案

以这种方式更新状态(不直接变异)是一种很好的做法 我发现有两个有效载荷action.gamesaction.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;

I'm new to react-redux and I'm having an issue with a web-app I'm developing. The app should have a user log-in functionality, it should be able to fetch and display a list of games from a database api I made, and it should be able to display information for a specific game from that list when it's clicked.

I have the user log-in functionality working perfectly, but the game list and specific game details don't initially display in the browser. If I look in the redux devtools, the action is dispatched and returns the correct information to the state, and if I page-through the devtools (push the play button), the list will show up in the dom, and stay until I refresh the page. The same is true for the game details.

I'm not sure what is wrong. I tried adjusting the react components and containers I'm using, but nothing I can think of / found in other posts seems to work. Maybe it's a problem with how I have the initial state set up (I have a initial state in both the user-login reducer and in the games reducer)?

I'll post what I think are the relevant blocks of code in this 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 (the root reducer file)

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 (the store composition file)

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 containers and components: 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 (houses the Hompage component which has the Gamelist container)

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 (the component that displays the GameList container)

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 (the container that calls the fetchGames action from the store to generate the list of games

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
);

I'll stop there because that's where the problem occurs for the first time. I am aware I have posted a lot of code, but I'm not sure what is relevant or irrelevant in this situation.

解决方案

It's a good practice to update the state this way don't mutate directly I find two payload action.games and action.game is that intentional or a typo?

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 dom不使用Redux存储更新的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆