【问题标题】:In presentational component, Redux Store state is undefined在展示组件中,Redux Store 状态未定义
【发布时间】:2021-10-07 18:51:31
【问题描述】:

我想在展示组件中访问 Redux Store State,但状态未定义。 我制作了 Container Component、Presentalal Component 和 Redux Store。

所以我尝试在演示组件中访问 user,但 user 未定义。

我在为异步 API 请求创建 Redux Store 时使用 Redux-Thunk。

令人怀疑的部分是 Rdux Store State 有一个值,但我无法访问演示组件中的状态 user

// headerState.js

import { handleActions } from 'redux-actions';
import * as api from '../lib/api';

const USER_STATE = 'headerState/USER_STATE';
const USER_STATE_SUCCESS = 'headerState/USER_STATE_SUCCESS';
const USER_STATE_FAILURE = 'headerState/USER_STATE_FAILURE';

const USER_LOGOUT = 'headerState/USER_LOGOUT';
const USER_LOGOUT_SUCCESS = 'headerState/USER_LOGOUT_SUCCESS';
const USER_LOGOUT_FAILURE = 'headerState/USER_LOGOUT_FAILURE';

export const getUserInfo = () => async (dispatch) => {
  dispatch({ type: USER_STATE });
  try {
    const response = await api.getUser();
    dispatch({
      type: USER_STATE_SUCCESS,
      payload: response.data,
    });
  } catch (error) {
    dispatch({
      type: USER_STATE_FAILURE,
      payload: error,
      error: true,
    });
    throw error;
  }
};

export const userLogout = () => async (dispatch) => {
  dispatch({ type: USER_LOGOUT });
  try {
    const response = await api.logout();
    dispatch({
      type: USER_LOGOUT_SUCCESS,
      payload: response.data,
    });
  } catch (error) {
    dispatch({
      type: USER_LOGOUT_FAILURE,
      payload: error,
      error: true,
    });
    throw error;
  }
};

const initialState = {
  user: null,
  loading: {
    isProcessing: false,
  },
};

const headerState = handleActions(
  {
    [USER_STATE]: (state) => ({
      ...state,
      loading: {
        ...state.loading,
        isProcessing: true,
      },
    }),
    [USER_STATE_SUCCESS]: (state, action) => ({
      ...state,
      user: action.payload,
      loading: {
        ...state.loading,
        isProcessing: false,
      },
    }),
    [USER_STATE_FAILURE]: (state) => ({
      ...state,
      loading: {
        ...state.loading,
        isProcessing: false,
      },
    }),
    [USER_LOGOUT]: (state) => ({
      ...state,
      loading: {
        ...state.loading,
        isProcessing: true,
      },
    }),
    [USER_LOGOUT_SUCCESS]: (state, action) => ({
      ...state,
      user: action.payload,
      loading: {
        ...state.loading,
        isProcessing: false,
      },
    }),
    [USER_LOGOUT_FAILURE]: (state) => ({
      ...state,
      loading: {
        ...state.loading,
        isProcessing: false,
      },
    }),
  },
  initialState,
);

export default headerState;

// HeaderContainer.js

import React, { useEffect } from 'react';
import { connect } from 'react-redux';
import Header from './common/Header';
import { getUserInfo, userLogout } from '../modules/headerState';

const HeaderContainer = ({ user, getUserInfo, userLogout }) => {
  useEffect(() => {
    getUserInfo();
  }, [getUserInfo]);

  return <Header user={user} logout={userLogout} />;
};

const mapStateToProps = (state) => {
  return {
    user: state.user,
    loading: state.loading,
  };
};

export default connect(mapStateToProps, {
  getUserInfo,
  userLogout,
})(HeaderContainer);

// Header.js

(...)

const Header = ({ user, logout }) => {  ** // user is undefined **
  return (
    <>
      <TopNav>
        <Link to="/" style={TitleLinkStyle}>
          <NavTitle>
            <img
              src={logo}
              width="50"
              height="50"
              color="white"
              style={{ paddingRight: '25px', alignSelf: 'center' }}
              alt="logo"
            />
            Service Title
          </NavTitle>
        </Link>
        <div style={{ display: 'flex', flex: 4 }} />
        <SubNav>
          <Link to="/home" style={{ textDecoration: 'none', color: 'white' }}>
            <HomeDiv>Home</HomeDiv>
          </Link>
          {!user.snsId && (
            <Link
              to="/login"
              style={{ textDecoration: 'none', color: 'white' }}
            >
              <LoginDiv>Login</LoginDiv>
            </Link>
          )}
          {user.snsId && (
            <LoginDiv onClick={logout} style={{ cursor: 'pointer' }}>
              Logout
            </LoginDiv>
          )}
        </SubNav>
      </TopNav>
    </>
  );
};

export default Header;

【问题讨论】:

  • user 的值是 null,直到 USER_STATE_SUCCESS 被调度。所以我想尝试在Header.js 中访问user.snsId 会导致类型错误。
  • @ksav 感谢您的想法。那么我可以通过在Header.js 的条件渲染中使用loading state 来解决这个问题吗?
  • 你也可以使用可选链developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… 例如user?.snsId
  • 感谢您的回答,谢谢。

标签: javascript reactjs react-redux react-thunk


【解决方案1】:

有两个潜在的问题。

首先,user 的初始值为 null,直到调度 USER_STATE_SUCCESS。所以尝试在你的演示组件中访问user.snsId会导致类型错误:

无法读取未定义的属性“snsId”

您可以使用可选链接来尝试访问snsId 并简化您的逻辑。

// Header.js
const Header = ({ user }) => {
  return user?.snsId ? <div>Logout</div> : <div>Login</div>;
};

第二个问题可能在于您如何创建和访问您的商店。我假设你正在做这样的事情:

const reducers = combineReducers({
  header: headerReducer
});
const store = createStore(reducers);

使用mapStateToProps 时,您的容器组件需要通过创建它时使用的相同键来访问该状态切片 - header

// HeaderContainer.js
const mapStateToProps = (state) => {
  return {
    user: state.header.user, // state.user is undefined
    loading: state.header.loading // state.loading is undefined
  };
};

【讨论】:

  • 您回答的第二个问题是我的潜在问题。非常感谢。
  • 不客气。在这种情况下,请随时点击 ✔ 接受
猜你喜欢
  • 2020-03-16
  • 1970-01-01
  • 2016-12-22
  • 2019-04-07
  • 1970-01-01
  • 1970-01-01
  • 2020-09-15
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多