Hello I want javascript -> typescript! but, so hard..
Help!!!
// 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 <- Is this how you do it? I've changed it a little bit.
// 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();
}
);
I saw the official document, but I don't understand. [https://github.com/kirill-konshin/next-redux-wrapper#getserversideprops]
These codes are not problematic in JavaScript. But there's a problem with typescripts.
I'm not good at English. Sorry..
So want Simple Codes, Simple Description.
Thanks.
Here are the issues that I see:
createStore(reducer, enhancer)
because your reducer
does not fit the type (state: State | undefined, action: AnyAction) => State
. You must make your reducer fit this type. The problem right now is that your reducer does not allow for state
to be undefined
.change
const rootReducer = (state: State, action: AnyAction) => {
to
const rootReducer = (state: State | undefined, action: AnyAction): State => {
store.sagaTask = sagaMiddleware.run(rootSaga);
because the store
object created by redux does not have a property called sagaTask
. There is another discussion about that here .Here is one solution, based on the next-redux-wrapper docs:
define a new interface for your store which includes the task
export interface SagaStore extends Store<State, AnyAction> {
sagaTask: Task;
}
replace
store.sagaTask = sagaMiddleware.run(rootSaga);
with
(store as SagaStore).sagaTask = sagaMiddleware.run(rootSaga);
replace
await context.store.sagaTask.toPromise();
with
await (context.store as SagaStore).sagaTask.toPromise();
I also had some bad time in trying to sync Redux with Next in a proper way, and them, using next-redux-wrapper, I created a template of a project, which is currently working and following the guidelines from next-redux-wrapper. You can take a look on that:
https://github.com/felipemeriga/next-typescript-redux-template
Basically I'm created the wrapper like this:
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;
Then overrided the _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);
And finally injected in the index.tsx component:
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(
({}) => {
}
);
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.