├── .babelrc
├── .gitignore
├── README.md
├── client
├── components
│ ├── Header.js
│ ├── Home.js
│ ├── Post.js
│ └── Products.js
├── index.js
├── routes.js
└── store
│ ├── actions
│ └── actions.js
│ ├── reducers
│ └── rootReducer.js
│ └── store.js
├── config.example.js
├── config.js
├── helper
└── template.js
├── package-lock.json
├── package.json
├── server
└── index.js
├── webpack.base.js
├── webpack.client.js
├── webpack.server.js
└── yarn.lock
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["@babel/preset-env", "@babel/preset-react"]
3 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea/
2 | *.log
3 | npm-debug.log
4 | yarn-error.log
5 | .DS_Store
6 | build/
7 | node_modules/
8 | dist/
9 | .cache
10 | config.js
11 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # React WooCommerce Theme :electric_plug:
2 | > * This is a React WooCommerce theme, built with Webpack, Babel, Node, Express, using WooCommerce REST API.
3 |
4 |
5 | ## Getting Started :rocket:
6 |
7 | These instructions will get you a copy of the project up and running on your local machine for development purposes.
8 |
9 | ### Prerequisites :page_facing_up:
10 |
11 |
12 | ### Installing :wrench:
13 |
14 | 1. Clone this repo in `git clone https://github.com/imranhsayed/react-woocommerce-theme`
15 | 2. `cd react-woocommerce-theme`
16 | 3. `npm install`
17 |
18 | ## Use :point_right:
19 |
20 | * Rename `congif.example.js` to `config.js` and update your WooCommerce config keys
21 |
22 | ```ruby
23 | const keys = {
24 | consumerKey: 'xxxxx',
25 | consumerSecret: 'xxxxx',
26 | };
27 | ```
28 |
29 | ## Common Commands :computer:
30 |
31 | * `dev` Runs webpack dev server in watch mode for both client and server in watch mode.
32 |
33 | ## Contributing :busts_in_silhouette:
34 |
35 | Please read [CONTRIBUTING.md](https://gist.github.com/PurpleBooth/b24679402957c63ec426) for details on our code of conduct, and the process for submitting pull requests to us.
36 |
37 | ## Versioning :bookmark_tabs:
38 |
39 | I use [Git](https://github.com/) for versioning.
40 |
41 | ## Author :bust_in_silhouette:
42 |
43 | * **[Imran Sayed](https://imransayed.com)**
44 |
45 | ## License :page_with_curl:
46 |
47 | This project is licensed under the MIT License - see the [LICENSE.md](LICENSE.md) file for details
48 |
--------------------------------------------------------------------------------
/client/components/Header.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { Link } from 'react-router-dom';
3 |
4 | class Header extends Component {
5 | render() {
6 | return (
7 |
8 | Home
9 | Post
10 | Products
11 |
12 | );
13 | }
14 | }
15 |
16 | export default Header;
17 |
--------------------------------------------------------------------------------
/client/components/Home.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 |
3 | class Home extends Component {
4 | render() {
5 | return (
6 |
7 | Hello world from React SSR. This is Home..
8 |
9 | );
10 | }
11 | }
12 |
13 | export default {
14 | component: Home
15 | };
16 |
--------------------------------------------------------------------------------
/client/components/Post.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { connect } from 'react-redux';
3 | import actions from '../store/actions/actions';
4 |
5 | class Post extends Component {
6 | componentDidMount() {
7 | this.props.dispatch(actions.getUserData())
8 | }
9 |
10 | render() {
11 | return (
12 |
13 | {
14 | this.props.user.login ?
{ this.props.user.login}
:
Loading...
15 | }
16 |
17 | );
18 | }
19 | }
20 |
21 | function mapStateToProps( state ) {
22 | return {
23 | user : state.user
24 | };
25 | }
26 |
27 | function loadData( store ) {
28 | return store.dispatch(actions.getUserData());
29 | }
30 |
31 | export default {
32 | component: connect( mapStateToProps )( Post ),
33 | loadData
34 | };
35 |
--------------------------------------------------------------------------------
/client/components/Products.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { connect } from 'react-redux';
3 | import actions from '../store/actions/actions';
4 |
5 | class Products extends Component {
6 | componentDidMount() {
7 | this.props.dispatch( actions.getProductsData() );
8 | }
9 |
10 | render() {
11 | const { products } = this.props;
12 | console.warn( products );
13 | return (
14 |
15 | { products.length && (
16 | products.map( item =>
{ item.name }
)
17 | ) }
18 |
19 | );
20 | }
21 | }
22 |
23 | function mapStateToProps( state ) {
24 | return {
25 | products : state.products
26 | };
27 | }
28 |
29 | function loadData( store ) {
30 | return store.dispatch(actions.getProductsData());
31 | }
32 |
33 | export default {
34 | component: connect( mapStateToProps )( Products ),
35 | loadData
36 | };
37 |
--------------------------------------------------------------------------------
/client/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import {BrowserRouter,} from 'react-router-dom';
4 | import { renderRoutes } from 'react-router-config';
5 | import {Provider} from 'react-redux';
6 |
7 | // Import Routes
8 | import routes from './routes';
9 |
10 | // Import store
11 | import store from './store/store';
12 | import Header from './components/Header';
13 |
14 | const JSX =() => (
15 |
16 |
17 | <>
18 |
19 |
20 | {
21 | renderRoutes(routes)
22 | }
23 |
24 | >
25 |
26 |
27 | )
28 |
29 | ReactDOM.hydrate(JSX(), document.getElementById('root'))
30 |
--------------------------------------------------------------------------------
/client/routes.js:
--------------------------------------------------------------------------------
1 | import Home from "./components/Home";
2 | import Post from "./components/Post";
3 | import Products from "./components/Products";
4 |
5 | export default [
6 | {
7 | ...Home,
8 | path: '/',
9 | exact: true
10 | },
11 | {
12 | ...Post,
13 | path: '/posts',
14 | exact: true,
15 | },
16 | {
17 | ...Products,
18 | path: '/products',
19 | exact: true,
20 | }
21 | ]
22 |
--------------------------------------------------------------------------------
/client/store/actions/actions.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios';
2 |
3 | const actions = {
4 | getUserData : () => dispatch => {
5 | axios.get('https://api.github.com/users/imranhsayed')
6 | .then( userData => {
7 | dispatch({
8 | type: "USER",
9 | user : userData.data,
10 | })
11 | })
12 | },
13 | getProductsData : () => dispatch => {
14 | axios.get('/getProducts')
15 | .then( productData => {
16 | dispatch({
17 | type: "PRODUCT",
18 | products : productData.data,
19 | })
20 | })
21 | }
22 | };
23 |
24 | export default actions;
25 |
--------------------------------------------------------------------------------
/client/store/reducers/rootReducer.js:
--------------------------------------------------------------------------------
1 | import actions from "../actions/actions";
2 |
3 | const initState = {
4 | user: {},
5 | products: {}
6 | };
7 |
8 | function rootReducer( state = initState, action ) {
9 | switch( action.type ) {
10 | case "USER" : return { user : action.user };
11 | case "PRODUCT" : return { products : action.products };
12 |
13 | default: return state;
14 | }
15 | }
16 |
17 | export default rootReducer;
18 |
--------------------------------------------------------------------------------
/client/store/store.js:
--------------------------------------------------------------------------------
1 | import { createStore, applyMiddleware, compose } from 'redux';
2 | import thunk from 'redux-thunk';
3 | import rootReducer from './reducers/rootReducer';
4 |
5 | const composeEnhancers =
6 | typeof window === 'object' &&
7 | window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ?
8 | window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({
9 | // Specify extension’s options like name, actionsBlacklist, actionsCreators, serialize...
10 | }) : compose;
11 |
12 | const store = createStore( rootReducer, composeEnhancers( applyMiddleware( thunk ) ) );
13 |
14 | export default store;
15 |
--------------------------------------------------------------------------------
/config.example.js:
--------------------------------------------------------------------------------
1 | const keys = {
2 | consumerKey: 'xxxxxx',
3 | consumerSecret: 'xxxxx',
4 | };
5 |
6 | export default keys;
7 |
--------------------------------------------------------------------------------
/config.js:
--------------------------------------------------------------------------------
1 | const keys = {
2 | consumerKey: 'ck_c798f49c41c39bb5f4e57fe406d0a4180cabc499',
3 | consumerSecret: 'cs_0f5f4521779f5972c26af2a0d2adfdebccd71400',
4 | };
5 |
6 | export default keys;
7 |
--------------------------------------------------------------------------------
/helper/template.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { renderToString } from "react-dom/server";
3 | import { renderRoutes } from 'react-router-config';
4 | import { StaticRouter } from 'react-router-dom';
5 | import routes from '../client/routes';
6 | import { Provider } from 'react-redux';
7 |
8 |
9 | export function template( path, store ) {
10 | const jsx = renderToString(
11 |
12 |
13 | { renderRoutes( routes ) }
14 |
15 |
16 | );
17 | return `
18 |
19 |
20 | ${ jsx }
21 |
22 |
23 |
24 | `
25 | }
26 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-redux-ssr",
3 | "version": "1.0.0",
4 | "main": "index.js",
5 | "license": "MIT",
6 | "scripts": {
7 | "dev": "npm-run-all --parallel dev:*",
8 | "dev:server": "webpack --config webpack.server.js --watch",
9 | "dev:express": "nodemon dist/server.bundle.js",
10 | "dev:client": "webpack --config webpack.client.js --watch"
11 | },
12 | "dependencies": {
13 | "@babel/core": "^7.3.4",
14 | "@babel/preset-env": "^7.3.4",
15 | "@babel/preset-react": "^7.0.0",
16 | "axios": "^0.18.0",
17 | "babel-loader": "^8.0.5",
18 | "express": "^4.16.4",
19 | "nodemon": "^1.18.10",
20 | "npm-run-all": "^4.1.5",
21 | "react": "^16.8.4",
22 | "react-dom": "^16.8.4",
23 | "react-redux": "^6.0.1",
24 | "react-router-config": "^4.4.0-beta.6",
25 | "react-router-dom": "^4.3.1",
26 | "redux": "^4.0.1",
27 | "redux-thunk": "^2.3.0",
28 | "webpack": "^4.29.6",
29 | "webpack-merge": "^4.2.1",
30 | "webpack-node-externals": "^1.7.2",
31 | "woocommerce-api": "^1.4.2"
32 | },
33 | "devDependencies": {
34 | "webpack-cli": "^3.2.3"
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/server/index.js:
--------------------------------------------------------------------------------
1 | import express from 'express';
2 | import { template } from '../helper/template';
3 | import store from '../client/store/store';
4 | import { matchRoutes } from 'react-router-config';
5 | import routes from '../client/routes';
6 | import WooCommerceAPI from 'woocommerce-api';
7 | import config from '../config';
8 |
9 | const app = express();
10 |
11 | // Set path for static files
12 | app.use( express.static( 'dist' ) );
13 |
14 | /**
15 | * Get All products
16 | */
17 | app.get( '/getProducts', ( request, response ) => {
18 | var WooCommerce = new WooCommerceAPI({
19 | url: 'https://www.orionhive.com/',
20 | consumerKey: config.consumerKey,
21 | consumerSecret: config.consumerSecret,
22 | wpAPI: true,
23 | version: 'wc/v3'
24 | });
25 |
26 | WooCommerce.get('products', function(err, data, res) {
27 | response.json( JSON.parse(res) );
28 | });
29 | } );
30 |
31 | app.get( '*', ( req, res ) => {
32 |
33 | // MATCHING ROUTES FOR FETCHING DATA
34 | const promises = matchRoutes( routes, req.path ).map( ( { route } ) => {
35 | return route.loadData ? route.loadData( store ) : null;
36 | });
37 |
38 | Promise.all( promises ).then(() => {
39 | res.send( template( req.path, store ) )
40 | });
41 | });
42 |
43 |
44 | app.listen( 3000, ( err ) => {
45 | console.log( `Server is running on http://localhost:3000` );
46 | });
47 |
--------------------------------------------------------------------------------
/webpack.base.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | mode: "development",
3 | module: {
4 | rules: [
5 | {
6 | test: /\.js?$/,
7 | loader: "babel-loader",
8 | exclude: /node_modules/
9 | }
10 | ]
11 | }
12 | }
--------------------------------------------------------------------------------
/webpack.client.js:
--------------------------------------------------------------------------------
1 | const path = require( 'path' );
2 | const merge = require( 'webpack-merge' );
3 | const baseConfig = require( './webpack.base' );
4 |
5 | const config = {
6 | target: "web",
7 | entry: "./client/index.js",
8 | output: {
9 | filename: "client.bundle.js",
10 | path: path.resolve( __dirname, "dist" )
11 | },
12 | };
13 |
14 | module.exports = merge( baseConfig, config );
15 |
--------------------------------------------------------------------------------
/webpack.server.js:
--------------------------------------------------------------------------------
1 | const path = require( 'path' );
2 | const merge = require( 'webpack-merge' );
3 | const baseConfig = require( './webpack.base' );
4 | const webpackNodeExternals = require( 'webpack-node-externals' );
5 |
6 | const config = {
7 | target: "node",
8 | entry: "./server/index.js",
9 | output: {
10 | filename: "server.bundle.js",
11 | path: path.resolve( __dirname, "dist" )
12 | },
13 | externals: [ webpackNodeExternals() ]
14 | };
15 |
16 | module.exports = merge( baseConfig, config );
17 |
--------------------------------------------------------------------------------