Skip to content

Commit

Permalink
Merge pull request #4 from MostafaRastegar/redux-ssr-request
Browse files Browse the repository at this point in the history
Add Redux ssr request
  • Loading branch information
MostafaRastegar authored Apr 16, 2022
2 parents 9fb590f + 168de75 commit 261f9cf
Show file tree
Hide file tree
Showing 11 changed files with 462 additions and 393 deletions.
33 changes: 33 additions & 0 deletions components/Pages/UsersSSR/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { useSelector } from 'react-redux';
import { usersSelectors } from 'store';
import { StyledUsersWrapper, StyledUsersTitle } from './styles';
import { RootStore } from 'store/interfaces';

const Users = () => {
const usersData = useSelector((state: RootStore) =>
usersSelectors.getUsersData(state),
);

const usersLoading = useSelector((state: RootStore) =>
usersSelectors.getUsersLoading(state),
);

return (
<>
<StyledUsersWrapper>
<StyledUsersTitle>List Of Names</StyledUsersTitle>
{usersLoading ? (
<span>Loading ....</span>
) : (
<div>
{usersData?.map((item: { id: number; userName: string }) => (
<p key={item.id}>{item?.userName}</p>
))}
</div>
)}
</StyledUsersWrapper>
</>
);
};

export default Users;
12 changes: 12 additions & 0 deletions components/Pages/UsersSSR/styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import styled from 'styled-components';
import { color, px } from 'constants/theme/helpers';

export const StyledUsersWrapper = styled.div`
position: relative;
background-color: ${color('green', 'light')};
padding: ${px(30)};
`;
export const StyledUsersTitle = styled.h3`
padding-bottom: ${px(30)};
color: ${color('blue', 'dark')};
`;
6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "mr-next-starter",
"version": "1.2.0",
"version": "1.3.0",
"private": true,
"scripts": {
"dev": "next dev",
Expand All @@ -15,8 +15,9 @@
"@types/redux": "^3.6.0",
"@types/redux-thunk": "^2.1.0",
"axios": "^0.21.1",
"js-cookie": "^3.0.1",
"next": "^12.1.1",
"next-redux-wrapper": "^6.0.2",
"next-redux-wrapper": "^7.0.5",
"prop-types": "^15.7.2",
"react": "17.0.2",
"react-dom": "17.0.2",
Expand All @@ -29,6 +30,7 @@
"styled-components": "^5.2.1"
},
"devDependencies": {
"@types/js-cookie": "^3.0.1",
"@types/node": "^16.11.11",
"@types/react": "^17.0.37",
"@types/styled-components": "^5.1.11",
Expand Down
27 changes: 10 additions & 17 deletions pages/_app.tsx
Original file line number Diff line number Diff line change
@@ -1,29 +1,22 @@
import { useStore } from 'store/store';
import { wrapper } from 'store/store';
import { AppProps } from 'next/app';
import { Provider } from 'react-redux';
import { persistStore } from 'redux-persist';
import { PersistGate } from 'redux-persist/integration/react';
// import { Provider } from 'react-redux';
// import { persistStore } from 'redux-persist';
// import { PersistGate } from 'redux-persist/integration/react';
import { ThemeProvider } from 'styled-components';

import defaultTheme from 'constants/theme';
import GlobalStyle from 'constants/theme/GlobalStyle';

const App = ({ Component, pageProps }: AppProps) => {
const store = useStore(pageProps.initialReduxState);
const persistor = persistStore(store, {}, () => {
persistor.persist();
});

return (
<Provider store={store}>
<PersistGate loading={<div>loading</div>} persistor={persistor}>
<ThemeProvider theme={defaultTheme}>
<GlobalStyle />
<Component {...pageProps} />
</ThemeProvider>
</PersistGate>
</Provider>
<ThemeProvider theme={defaultTheme}>
<GlobalStyle />
<Component {...pageProps} />
</ThemeProvider>

);
};

export default App;
export default wrapper.withRedux(App);
25 changes: 25 additions & 0 deletions pages/ssr.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import React from 'react';
import type {
GetStaticPropsResult,
} from 'next';
import styled from 'styled-components';
import { usersEffects } from 'store';
import { wrapper } from 'store/store';
import Users from 'components/Pages/UsersSSR';

const Title = styled.h1`
color: red;
`;

const MainPage = () => (
<div>
<Title>My First Next.js Page</Title>
<Users />
</div>
);

export const getServerSideProps = wrapper.getServerSideProps((store) => async (): Promise<GetStaticPropsResult<{}>> => {
await store.dispatch(usersEffects.getUsersRequest() as any);
return { props: {} };
})
export default MainPage;
71 changes: 0 additions & 71 deletions store/request.ts

This file was deleted.

14 changes: 14 additions & 0 deletions store/request/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import axios from 'axios';
import { setupInterceptorsTo } from './interceptors';

const request = setupInterceptorsTo(
axios.create({
headers: {
'Content-Type': 'application/json',
},
}),
);

export const requestWithotAuth = setupInterceptorsTo(axios.create());

export default request;
76 changes: 76 additions & 0 deletions store/request/interceptors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import axios, {
AxiosError,
AxiosInstance,
AxiosRequestConfig,
AxiosResponse,
} from 'axios';
import Cookies from 'js-cookie';

// import { useDispatch } from 'react-redux';
// import { customerActions } from 'store';

const onRequest = (config: AxiosRequestConfig): AxiosRequestConfig => {
const accessToken = Cookies.get('accessToken');
if (accessToken && accessToken !== 'null') {
config.headers['Authorization'] = `Bearer ${accessToken}`;
} else {
delete config.headers['Authorization'];
}

return config;
};

const onRequestError = (error: AxiosError): Promise<AxiosError> => {
return Promise.reject(error);
};

const onResponse = (response: AxiosResponse): AxiosResponse => {
return response;
};

const onResponseError = async (error: any): Promise<any> => {
// console.log('error.response:>> ', error.response);
// dispatch(customerActions.postTokenWebLoginFailure(errObject(error.response)));
if (!error.response) {
return {
status: 503,
data: null,
problem: 'Network Error',
};
}
if (error.response) {
if (error.response.status === 403) {
console.log('please login :>> ');
}
if (error.response.status === 401) {
// Access Token was expired
const accessToken = Cookies.get('accessToken');
const refreshToken = Cookies.get('refreshToken');

try {
const rs = await axios.post('/refresh-token-api', {
RefreshToken: refreshToken,
AccessToken: accessToken,
});

const { AccessToken, RefreshToken } = rs.data;

Cookies.set('accessToken', AccessToken);
Cookies.set('refreshToken', RefreshToken);

return rs.data;
} catch (_error) {
return Promise.reject(_error);
}
}
}
return error.response;
};

export const setupInterceptorsTo = (
axiosInstance: AxiosInstance,
): AxiosInstance => {
axiosInstance.interceptors.request.use(onRequest, onRequestError);
axiosInstance.interceptors.response.use(onResponse, onResponseError);
return axiosInstance;
};
73 changes: 22 additions & 51 deletions store/store.ts
Original file line number Diff line number Diff line change
@@ -1,60 +1,31 @@
import { useMemo } from 'react';
import { createStore, applyMiddleware } from 'redux';
import { composeWithDevTools } from 'redux-devtools-extension';
import { persistReducer } from 'redux-persist';
import storage from 'redux-persist/lib/storage';
import { createStore, applyMiddleware, AnyAction } from 'redux';
import { createWrapper, HYDRATE } from "next-redux-wrapper";
import { loadingBarMiddleware } from 'react-redux-loading-bar';
import thunk from 'redux-thunk';
import rootReducer, { RootReducerI } from './rootReducer';
import rootReducer from './rootReducer';

let store: any;

// add redux loading bar middleware
const loadingMD = loadingBarMiddleware({
promiseTypeSuffixes: ['REQUEST', 'SUCCESS', 'FAILURE'],
});

const persistConfig = {
key: 'primary',
storage,
// whitelist: [], // place to select which state you want to persist
const bindMiddleware = (middleware: any[]) => {
if (process.env.NODE_ENV !== "production") {
const { composeWithDevTools } = require("redux-devtools-extension");
return composeWithDevTools(applyMiddleware(...middleware));
}
return applyMiddleware(...middleware);
};

const persistedReducer = persistReducer(persistConfig, rootReducer);

function makeStore(initialState: RootReducerI) {
return createStore(
persistedReducer,
initialState,
composeWithDevTools(applyMiddleware(thunk, loadingMD)),
);
}

export const initializeStore = (preloadedState: RootReducerI) => {
let _store = store ?? makeStore(preloadedState);

// After navigating to a page with an initial Redux state, merge that state
// with the current state in the store, and create a new store
if (preloadedState && store) {
_store = makeStore({
...store.getState(),
...preloadedState,
});
// Reset the current store
store = undefined;
const reducer = (state: any, action: AnyAction) => {
if(action.type === HYDRATE) {
const nextState = {
...state,
...action.payload,
}
return nextState;
}
return rootReducer(state, action);

// For SSG and SSR always create a new store
if (typeof window === 'undefined') return _store;
// Create the store once in the client
if (!store) store = _store;

return _store;
};
}

export function useStore(initialState: RootReducerI) {
const memoedStore = useMemo(() => initializeStore(initialState), [
initialState,
]);
return memoedStore;
const initStore = () => {
return createStore(reducer, bindMiddleware([thunk, loadingBarMiddleware()]));
}

export const wrapper = createWrapper(initStore);
Loading

0 comments on commit 261f9cf

Please sign in to comment.