我目前正在使用Redux, Redux Thunk with NextJS,并试图找出如何访问功能组件的函数内更新的Redux状态。
从下面的代码中可以看到,在handleSubmit
函数中,我想要更新redux状态,然后检查状态值并决定它应该将用户带到哪条路由。
以前在我的旧项目中,使用mapStateToProps
与Class组件,我能够访问我的handleSubmit
函数内更新的redux状态,但是当使用功能组件时,两个选项(useSelector
钩子或mapStateToProps
与connect()
)似乎不起作用。
起初我认为组件没有重新渲染,但是当检查useEffect()
中的状态时,我可以看到状态正在更新,组件能够查看更新的值。
是否有我明显遗漏的东西,或者这种方式对于功能组件是不可能的?
loginPage.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;
我已经附加了存储和减速器,以防我设置错误。
store.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 });
reducer.tsx
import Auth from 'lib/interfaces/auth';
import { Reducer } from 'redux';
import { ActionTypes, AuthAction } from './actions';
const reducer: Reducer<Auth, AuthAction> = (state: Auth = {} as Auth, action: AuthAction): Auth => {
switch (action.type) {
case ActionTypes.UpdateToken:
return Object.assign({}, state, { token: action.token });
case ActionTypes.UpdateRefreshToken:
return Object.assign({}, state, { refreshToken: action.refreshToken });
case ActionTypes.UpdateParsedJwt:
return Object.assign({}, state, { parsedJwt: action.parsedJwt });
case ActionTypes.UpdateUuid:
return Object.assign({}, state, { uuid: action.uuid });
default:
return state;
}
};
export default reducer;
我认为最简单的解决方案是从'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;