Skip to content

Commit

Permalink
Merge pull request #1 from yussan/development
Browse files Browse the repository at this point in the history
feat(first release): ready to use
  • Loading branch information
yussan authored Nov 20, 2020
2 parents 733a7ca + 1fa94e5 commit 5d48f0f
Show file tree
Hide file tree
Showing 11 changed files with 254 additions and 51 deletions.
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,13 @@ Apps will run on port 20203
npm run build && npm run start
```

## REDUX Access

Using react-redux for packaging, you are free to use your favorite redux middleware, just add it according to the documentation below.

**Middlewares**
Setup your custom middleware on `./redux/store`

## Refs

- NextJS : https://nextjs.org/
Expand Down
47 changes: 47 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
"dependencies": {
"next": "10.0.2",
"react": "17.0.1",
"react-dom": "17.0.1"
"react-dom": "17.0.1",
"react-redux": "^7.2.2",
"redux": "^4.0.5",
"redux-logger": "^3.0.6"
}
}
24 changes: 20 additions & 4 deletions pages/_app.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,23 @@
import '../styles/globals.css'
import "../styles/globals.css";
import { Provider } from "react-redux";
import WithRedux from "../redux/withRedux";

function MyApp({ Component, pageProps }) {
return <Component {...pageProps} />
function MyApp(props) {
const { pageProps, Component, reduxStore } = props;
return (
<Provider store={reduxStore}>
<Component {...pageProps} />
</Provider>
);
}

export default MyApp
MyApp.getInitialProps = async function ({ Component, ctx }) {
let pageProps = Component.getInitialProps
? await Component.getInitialProps(ctx)
: {};

//Anything returned here can be access by the client
return { pageProps };
};

export default WithRedux(MyApp);
102 changes: 56 additions & 46 deletions pages/index.js
Original file line number Diff line number Diff line change
@@ -1,65 +1,75 @@
import Head from 'next/head'
import styles from '../styles/Home.module.css'
import Head from "next/head";
import styles from "../styles/Home.module.css";
import { connect } from "react-redux";
import { fetchApi } from "../redux/modules/posts/actions";

export default function Home() {
const HomePage = ({ posts, dispatch }) => {
return (
<div className={styles.container}>
<Head>
<title>Create Next App</title>
<title>Next Redux Skeleton</title>
<link rel="icon" href="/favicon.ico" />
</Head>

<main className={styles.main}>
<h1 className={styles.title}>
Welcome to <a href="https://nextjs.org">Next.js!</a>
</h1>

<p className={styles.description}>
Get started by editing{' '}
<code className={styles.code}>pages/index.js</code>
<h1>Ready to use</h1>
<p>
On this section bellow is an example of a Github api call from the
client or server side, and save result to Redux store.
</p>

<div className={styles.grid}>
<a href="https://nextjs.org/docs" className={styles.card}>
<h3>Documentation &rarr;</h3>
<p>Find in-depth information about Next.js features and API.</p>
</a>

<a href="https://nextjs.org/learn" className={styles.card}>
<h3>Learn &rarr;</h3>
<p>Learn about Next.js in an interactive course with quizzes!</p>
</a>

<p>
<a
href="https://github.com/vercel/next.js/tree/master/examples"
className={styles.card}
>
<h3>Examples &rarr;</h3>
<p>Discover and deploy boilerplate example Next.js projects.</p>
</a>
href="#"
onClick={(e) => {
e.preventDefault();
dispatch(fetchApi());
setTimeout(() => {
// simulate done api request

<a
href="https://vercel.com/import?filter=next.js&utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
className={styles.card}
// sample dummy response from api
const response = { status: 200, message: "success" };

//sample call api ad fill redux store on server side
dispatch(fetchApi(response));
}, 1500);
}}
>
<h3>Deploy &rarr;</h3>
<p>
Instantly deploy your Next.js site to a public URL with Vercel.
</p>
Click to recall API
</a>
</div>
</p>
<p>
{!posts.response || posts.is_loading ? (
<span style={{ color: "F4F4F4" }}>loading...</span>
) : (
JSON.stringify(posts.response)
)}
</p>
</main>

<footer className={styles.footer}>
<a
href="https://vercel.com?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
Powered by{' '}
<img src="/vercel.svg" alt="Vercel Logo" className={styles.logo} />
<a href="https://github.com/yussan/next-redux-skeleton">
Collaboration here
</a>
</footer>
</div>
)
}
);
};

HomePage.getInitialProps = async ({ req, query, reduxStore }) => {
if (req) {
// sample dummy response from api
const response = { status: 200, message: "success" };

//sample call api ad fill redux store on server side
await reduxStore.dispatch(fetchApi(response));
}
return {
query,
};
};

export default connect((state) => {
return {
posts: state.Posts,
};
})(HomePage);
17 changes: 17 additions & 0 deletions redux/modules/posts/actions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { POSTS_REQ_DONE, POSTS_REQ_WAITING } from "./types";

/**
* sample function to fetch api
*/
export function fetchApi(response) {
if (!response) {
return {
type: POSTS_REQ_WAITING,
};
} else {
return {
type: POSTS_REQ_DONE,
response,
};
}
}
14 changes: 14 additions & 0 deletions redux/modules/posts/reducer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { POSTS_REQ_DONE, POSTS_REQ_WAITING } from "./types";

const PostsReducer = (state = {}, action) => {
switch (action.type) {
case POSTS_REQ_WAITING:
return { ...{ loading: true } };
case POSTS_REQ_DONE:
return { ...{ response: action.response || {}, loading: false } };
default:
return state;
}
};

export default PostsReducer;
4 changes: 4 additions & 0 deletions redux/modules/posts/types.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// type to handle post request is on going / waiting
export const POSTS_REQ_WAITING = "POSTS_REQ_WAITING";
// type to handle post request is done, both success / error
export const POSTS_REQ_DONE = "POSTS_REQ_DONE";
8 changes: 8 additions & 0 deletions redux/rootReducer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { combineReducers } from "redux";

// available reducers
import PostsReducer from "./modules/posts/reducer";

export default combineReducers({
Posts: PostsReducer,
});
22 changes: 22 additions & 0 deletions redux/store.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { createStore, applyMiddleware } from "redux";
import rootReducer from "./rootReducer";
import getConfig from "next/config";

const { publicRuntimeConfig } = getConfig();

// setup your middlewares here
let Middlewares = applyMiddleware();

if (
publicRuntimeConfig.NODE_ENV === "production" &&
typeof window !== "undefined"
) {
// redux logger, available on browser console
const { createLogger } = require("redux-logger");
const loggerMiddleware = createLogger({});
Middlewares = applyMiddleware(loggerMiddleware);
}

export function initializeStore(initialState = {}) {
return createStore(rootReducer, initialState, Middlewares);
}
55 changes: 55 additions & 0 deletions redux/withRedux.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// redux wrapper for app
import React from "react";
import { initializeStore } from "./store";

const isServer = typeof window === "undefined";
const __STORE__ = "__STORE__";

function getOrCreateStore(initialState) {
// Always make a new store if server, otherwise state is shared between requests
if (isServer) {
return initializeStore(initialState);
}

// Create store if unavailable on the client and set it on the window object
if (!window[__STORE__]) {
window[__STORE__] = initializeStore(initialState);
}

return window[__STORE__];
}

const WithRedux = (App) => {
return class AppWithRedux extends React.Component {
constructor(props) {
super(props);
this.reduxStore = getOrCreateStore(props.initialReduxState);
}

static async getInitialProps(appContext) {
let reduxInitialState = {};
// Get or Create the store with `undefined` as initialState
// This allows you to set a custom default initialState
const reduxStore = getOrCreateStore(reduxInitialState);

// Provide the store to getInitialProps of pages
appContext.ctx.reduxStore = reduxStore;

let appProps = {};
if (typeof App.getInitialProps === "function") {
appProps = await App.getInitialProps(appContext);
}

return {
...appProps,
initialReduxState: reduxStore.getState(),
};
}

render() {
return <App {...this.props} reduxStore={this.reduxStore} />;
}
};
};

export default WithRedux;

0 comments on commit 5d48f0f

Please sign in to comment.