如何使用 typescript、next-redux-wrapper、getServerSideProps?
Posted
技术标签:
【中文标题】如何使用 typescript、next-redux-wrapper、getServerSideProps?【英文标题】:how to use typescript, next-redux-wrapper, getServerSideProps? 【发布时间】:2021-01-16 05:31:02 【问题描述】:我想要 javascript -> TypeScript!但是,太难了。
// store.js
import applyMiddleware, createStore, compose, Store from "redux";
import createSagaMiddleware, Task from "redux-saga";
import createWrapper from "next-redux-wrapper";
import composeWithDevTools from "redux-devtools-extension";
import reducer from "./reducers";
import rootSaga from "./sagas";
const configureStore = () =>
const sagaMiddleware = createSagaMiddleware();
const middlewares = [sagaMiddleware];
const enhancer =
process.env.NODE_ENV === "production"
? compose(applyMiddleware(...middlewares))
: composeWithDevTools(applyMiddleware(...middlewares));
const store = createStore(reducer, enhancer);
store.sagaTask = sagaMiddleware.run(rootSaga);
return store;
;
const wrapper = createWrapper(configureStore,
debug: process.env.NODE_ENV === "development",
);
export default wrapper;
// reducers/index.ts
import HYDRATE from "next-redux-wrapper";
import AnyAction, combineReducers from "redux";
import url, IUrlReducerState from "./reducer_url";
import user, IUserReducerState from "./reducer_user";
export type State =
url: IUrlReducerState;
user: IUserReducerState;
;
const rootReducer = (state: State, action: AnyAction) =>
switch (action.type)
case HYDRATE:
return action.payload;
default:
const combineReducer = combineReducers(
url,
user,
);
return combineReducer(state, action);
;
export type RootState = ReturnType<typeof rootReducer>;
export default rootReducer;
reducers/index.ts
// pages/index.js
import END from "redux-saga";
import wrapper from "../store";
export const getServerSideProps = wrapper.getServerSideProps(
async (context) =>
context.store.dispatch(
type: LOAD_USER_REQUEST,
);
context.store.dispatch(END);
await context.store.sagaTask.toPromise();
);
看了官方文档,没看懂:https://github.com/kirill-konshin/next-redux-wrapper#getserversideprops
这些代码在 JavaScript 中没有问题。 但是 TypeScript 有问题。
【问题讨论】:
您正在尝试解决的具体问题是什么?您可以链接到包含完整项目的演示或 repo 吗? 【参考方案1】:以下是我看到的问题:
-
您将在
createStore(reducer, enhancer)
中收到错误,因为您的reducer
不适合(state: State | undefined, action: AnyAction) => State
类型。您必须使您的减速机适合这种类型。现在的问题是您的减速器不允许 state
成为 undefined
。
改变
const rootReducer = (state: State, action: AnyAction) =>
到
const rootReducer = (state: State | undefined, action: AnyAction): State =>
store.sagaTask = sagaMiddleware.run(rootSaga);
行会出现错误,因为 redux 创建的 store
对象没有名为 sagaTask
的属性。还有另一个关于 here 的讨论。
这是一个基于 next-redux-wrapper 文档的解决方案:
为您的商店定义一个包含任务的新界面
export interface SagaStore extends Store<State, AnyAction>
sagaTask: Task;
替换
store.sagaTask = sagaMiddleware.run(rootSaga);
与
(store as SagaStore).sagaTask = sagaMiddleware.run(rootSaga);
替换
await context.store.sagaTask.toPromise();
与
await (context.store as SagaStore).sagaTask.toPromise();
【讨论】:
天哪!非常感谢!!!!我解决了这个问题。 不确定为什么 Redux Saga 不为此提供默认的 TypeScript 类型。 +1。 这个库似乎使用了一些不推荐使用的方法,请查看 github repo 中的问题部分 当在getServerSideProps
中使用await (store as SagaStore).sagaTask.toPromise();
时,我的页面永远加载,你能告诉我为什么吗?【参考方案2】:
我在尝试以适当的方式将 Redux 与 Next 同步时也遇到了一些糟糕的情况,他们使用 next-redux-wrapper 创建了一个项目模板,该模板目前正在工作并遵循 next- redux 包装器。你可以看看:
https://github.com/felipemeriga/next-typescript-redux-template
基本上我是这样创建包装器的:
const thunkMiddleware = thunk.withExtraArgument() as ThunkMiddleware<IStoreState, AnyAction>;
// create a makeStore function
// This makeStore is needed for the wrapper, for every new page that is called, a new store with the current values will be created
const makeStore: MakeStore<IStoreState> = (context: Context) => createStore(reducers, composeWithDevTools(applyMiddleware(thunkMiddleware)));
export type ExtraArgument = ;
export type ThunkCreator<R = Promise<any>> = ActionCreator<ThunkAction<R, IStoreState, ExtraArgument, AnyAction>>;
// export an assembled wrapper
// this wrapper will be used to every page's component, for injecting the store and actions into it.
const wrapper = createWrapper<IStoreState>(makeStore, debug: false);
export default wrapper;
然后覆盖了_app.tsx:
// For default you don't need to edit _app.tsx, but if you want to wrapper the pages with redux wrapper, you need
// to override _app.tsx with this code bellow
class MyApp extends App
// @ts-ignore
static async getInitialProps(Component, ctx)
return
pageProps:
// Call page-level getInitialProps
...(Component.getInitialProps ? await Component.getInitialProps(ctx) : ),
;
render()
const Component, pageProps = this.props;
return (
<Component ...pageProps />
);
export default wrapper.withRedux(MyApp);
最后注入到 index.tsx 组件中:
interface IProps
tick: ITickState
updateAnnouncement: any
interface IState
interface IDispatchProps
onUpdateTick: (message: string) => ITickState,
thunkAsyncFunction: () => Promise<any>;
type Props = IProps & IState & IDispatchProps
class App extends React.Component<Props>
constructor(props: Props)
super(props);
async componentWillUnmount(): Promise<void>
await this.props.thunkAsyncFunction();
render()
return (
<Layout title="Home | Next.js + TypeScript Example">
<h1>Hello Next.js ?</h1>
<p>
<Link href="/about">
<a>About</a>
</Link>
</p>
<div>
The current tick state: this.props.tick.message
</div>
</Layout>
);
const mapStateToProps = (state: IStoreState): tick: ITickState => (
tick: getTickState(state)
);
const mapDispatchToProps = (dispatch: any): IDispatchProps =>
return
onUpdateTick: (message: string) =>
dispatch(updateTick(message)),
thunkAsyncFunction: () => dispatch(thunkAsyncFunction())
;
export default connect(mapStateToProps, mapDispatchToProps)(App);
// As the wrapper is injected in _app.tsx, for every component(page) that will interact with Redux and Thunk
// you need to place this piece of code bellow, that will get the static props from the wrapper, and inject on your
// component
export const getStaticProps = wrapper.getStaticProps(
() =>
);
【讨论】:
以上是关于如何使用 typescript、next-redux-wrapper、getServerSideProps?的主要内容,如果未能解决你的问题,请参考以下文章
对 React 中如何使用接口(TypeScript)感到困惑
如何测试使用 jasmine + TypeScript 使用常量调用的函数