即使重新渲染,函数也不会收到更新的 redux 状态

时间:2021-01-18 02:28:38

标签: javascript reactjs redux react-redux redux-thunk

我目前正在使用 Redux、Redux Thunk 和 NextJS,并一直试图找出如何在功能组件的函数内访问更新的 redux 状态。

正如您在下面的代码中看到的,在 handleSubmit 函数中,我想更新 redux 状态,然后检查状态值并决定它应该将用户带到哪条路线。

以前在我的旧项目中,使用 mapStateToProps 和 Class 组件,我能够访问我的 handleSubmit 函数内更新的 redux 状态,但是当使用功能组件时,两个选项({{1} } hook 或 useSelector with mapStateToProps) 似乎不起作用。

起初我以为组件没有重新渲染,但是在检查 connect() 中的状态时,我可以看到状态正在更新,并且组件能够查看更新后的值。

是否有我明显遗漏的东西,或者功能组件无法采用这种方式?

loginPage.tsx

useEffect()

我附上了 store 和 reducer,以防我设置错误。

store.tsx

import LoginForm, { FormData } from 'components/Forms/LoginForm';
import Layout from 'components/Layout';
import { FORM_ERROR } from 'final-form';
import StatusCodes from 'lib/enums/statusCodes';
import { storeAuthToken } from 'lib/helpers/auth';
import { useRouter } from 'next/router';
import React, { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { ApplicationState } from 'redux/store';

const LoginPage = () => {
  const router = useRouter();
  const dispatch = useDispatch();
  const { auth } = useSelector((state: ApplicationState) => ({ auth: state.auth }));

  const handleSubmit = async (values: FormData) => {
    if (values && values.username && values.password) {
      try {
        // Updates redux store
        await storeAuthToken(dispatch, values.username, values.password);
      } catch (error) {
        if (error === StatusCodes.BadRequest) {
          return { [FORM_ERROR]: 'Sorry, you have entered incorrect details. Please try again' };
        } else {
          return { [FORM_ERROR]: 'Sorry, there was an issue trying to log you in' };
        }
      }

      // Can't see updated values
      console.log('Auth: ', auth);

      if (auth.parsedJwt && auth.parsedJwt.changePassword) {
        router.push({ pathname: '/update-password' });
        return;
      }

      router.push('/dashboard');
    }
  };

  useEffect(() => {
    // Can see the updated values
    console.log('New Auth: ', auth);
  }, [auth]);

  return (
    <Layout hideProfileMenu title="Login">
      <LoginForm onSubmit={handleSubmit} />
    </Layout>
  );
};

export default LoginPage;

reducer.tsx

import Auth from 'lib/interfaces/auth';
import { Context, createWrapper, HYDRATE, MakeStore } from 'next-redux-wrapper';
import { AnyAction, applyMiddleware, CombinedState, combineReducers, createStore, Dispatch, Reducer } from 'redux';
import thunkMiddleware, { ThunkAction, ThunkDispatch, ThunkMiddleware } from 'redux-thunk';
import authReducer from './auth/reducer';

export interface ApplicationState {
  auth: Auth
}

const isDebug = process.env.NODE_ENV !== 'production';

const bindMiddleware = (middleware: ThunkMiddleware) => {
  if (isDebug) {
    const { composeWithDevTools } = require('redux-devtools-extension');
    return composeWithDevTools(applyMiddleware(middleware));
  }

  return applyMiddleware(middleware);
};

const combinedReducer: Reducer<ApplicationState> = combineReducers<ApplicationState>({
  auth: authReducer
});

const reducer = (state: ApplicationState, action: AnyAction) => {
  if (action.type === HYDRATE) {
    const nextState: CombinedState<ApplicationState> = {
      ...state,
      ...action.payload
    };

    return nextState;
  } else {
    return combinedReducer(state, action);
  }
};

const makeStore: MakeStore<ApplicationState> = (_context: Context) => createStore(reducer as Reducer<ApplicationState, AnyAction>, bindMiddleware(thunkMiddleware));

export const wrapper = createWrapper<ApplicationState>(makeStore, { debug: isDebug });

1 个答案:

答案 0 :(得分:1)

我认为最简单的解决方案是从 'storeAuthToken' 返回 auth 对象,因为它保持错误处理并产生相同的逻辑流,并修复了注释中发现的异步问题。

import LoginForm, { FormData } from 'components/Forms/LoginForm';
import Layout from 'components/Layout';
import { FORM_ERROR } from 'final-form';
import StatusCodes from 'lib/enums/statusCodes';
import { storeAuthToken } from 'lib/helpers/auth';
import { useRouter } from 'next/router';
import React, { useEffect } from 'react';
import { useDispatch } from 'react-redux';
import { ApplicationState } from 'redux/store';

const LoginPage = () => {
  const router = useRouter();
  const dispatch = useDispatch();
  
  const handleSubmit = async (values: FormData) => {
    if (values && values.username && values.password) {
      try {
        // Updates redux store
        const authResult = await storeAuthToken(dispatch, values.username, values.password);
          
        if (authResult.parsedJwt && authResult.parsedJwt.changePassword) {
          router.push({ pathname: '/update-password' });
          return;
        }
        
      } catch (error) {
        if (error === StatusCodes.BadRequest) {
          return { [FORM_ERROR]: 'Sorry, you have entered incorrect details. Please try again' };
        } else {
          return { [FORM_ERROR]: 'Sorry, there was an issue trying to log you in' };
        }
      }

      router.push('/dashboard');
    }
  };

  return (
    <Layout hideProfileMenu title="Login">
      <LoginForm onSubmit={handleSubmit} />
    </Layout>
  );
};

export default LoginPage;