在展示组件中,Redux Store 状态未定义

In presentational component, Redux Store state is undefined

我想访问展示组件中的 Redux Store State,但状态未定义。 我制作了 Container Component、Presentational 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被调度。因此,尝试在您的演示组件中访问 user.snsId 将导致类型错误:

Cannot read property 'snsId' of undefined

您可以使用可选链接来尝试访问 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
  };
};