Creating Files

rhc provide you more flexibility and easy to use approach to create your files and folders for your componets, pages and redux implementation.

The following points shows how to use the create command.

Components

  1. To create your components simply run:
rhc create -c <component-name>

Example

rhc create -c test-component
src
└───components
    └───test-component
            index.jsx
            styles.css
import "./styles.css";

const TestComponent = () => {
  return <div>TestComponent component created!</div>;
};
export default TestComponent;
/* TestComponent styles */
  1. To create multiple components simply run:
rhc create -c <component-name-1> <component-name-2> ...

Exmaple

rhc create -c comp-1 comp-2
src
└───components
    ├───comp-1
    │       index.jsx
    │       styles.css
    │
    └───comp-2
            index.jsx
            styles.css
  1. To create one or mutliple components in a specified path that resides under the src/components/ folder, simply run:
rhc create -c <component-name-1> <component-name-2> ... -f <folder-path>

Example

rhc create -c comp-1 comp-2 -f foo/bar
src
└───components
    └───foo
        └───bar
            ├───comp-1
            │       index.jsx
            │       styles.css
            │
            └───comp-2
                    index.jsx
                    styles.css

Pages

  1. To create your page simply run:
rhc create -p <page-name>

Example

rhc create -p test-page
src
pages
    └───test-page
        │   index.jsx
        │   styles.css
        │
        └───functions
                index.js
import "./styles.css";

const TestPage = () => {
  return <div>TestPage page created!</div>;
};
export default TestPage;
/* TestPage styles */
// write your TestPage functions here
  1. To create multiple pages simply run:
rhc create -p <page-name-1> <page-name-2> ...

Example

rhc create -p page-1 page-2
src
pages
    ├───page-1
    │   │   index.jsx
    │   │   styles.css
    │   │
    │   └───functions
    │           index.js
    │
    └───page-2
        │   index.jsx
        │   styles.css
        │
        └───functions
                index.js
  1. To create one or multiple pages in a specific path that resides under src/pages/ folder, simply run:
rhc create -p <page-name-1> <page-name-2> ... -f <folder-path>

Example

rhc create -p page-1 page-2 -f foo/bar
src
└───pages
    └───foo
        └───bar
            ├───page-1
            │   │   index.jsx
            │   │   styles.css
            │   │
            │   └───functions
            │           index.js
            │
            └───page-2
                │   index.jsx
                │   styles.css
                │
                └───functions
                        index.js

Templates

You can create your pages and components with your defined templates by following these steps:

  1. First thing to do is to create a .template folder at the root of your react project.

  2. Inside the .template folder you can add your template, for example componentWithUseEffect.tsx (the file extension doesn’t matter so it could be *.jsx, *.js or *.tsx):

export default function __COMPONENT__() {
  useEffect(() => {}, []);

  return (
    <div>
      <h1>Hello, World!</h1>
    </div>
  );
}
  1. After creating your template you can use them to create components or pages as the following:
rhc create -c <component-name> -t <template-name>
rhc create -p <page-name> -t <template-name>

Example

As for our example it can be used like this for the above template:

rhc create -c comp -t componentWithUseEffect

Redux

rhc create -r
src
└───redux
    │   index.js
    │
    ├───actions
    │   └───general
    │           index.js
    │
    └───reducers
        │   index.js
        │
        └───general
                index.js
import { applyMiddleware, compose, createStore } from "redux";
import { mainReducer } from "./reducers";

/**
 * the main redux state, with all the reducers
 */
export const mainStore = createStore(
  mainReducer,
  compose(applyMiddleware(thunk))
);

/**
 * Creates a new redux state each time this function is called, this is used only for unit tests, to ensure that we have fresh state on each individual test
 */
export const createMainStore = () => {
  return createStore(mainReducer, compose(applyMiddleware(thunk)));
};
// write your general actions here

// this is an example for an action
export const init = () => async (dispatch, getState) => {
  dispatch({ type: "UPDATE_GENERAL", payload: { message: "init created!" } });
};
const initialState = { message: "" };

export const general = (state = initialState, action) => {
  switch (action.type) {
    case "UPDATE_GENERAL":
      return { ...state, ...action.payload };
    default:
      return state;
  }
};
import { combineReducers } from "redux";
import { general } from "./general";

export const mainReducer = combineReducers({
  general,
});

redux/index.ts

import { applyMiddleware, compose, createStore } from "redux";
import { mainReducer } from "./reducers";

/**
 * the main redux state, with all the reducers
 */
export const mainStore = createStore(
  mainReducer,
  compose(applyMiddleware(thunk))
);

export type StateInterface = ReturnType<typeof mainStore.getState>;

/**
 * list of action types
 */
export type ActionType = "UPDATE_GENERAL";

export interface Action<T> {
  type: ActionType;
  payload: Partial<T>;
}

export type ThunkResult<
  A = Record<string, unknown>,
  E = Record<string, unknown>
> = ThunkAction<void, StateInterface, E, Action<A>>;

export type Dispatch<A> = ThunkDispatch<
  StateInterface,
  Record<string, unknown>,
  Action<A>
>;

redux/actions/general/index.ts

import { GeneralState } from "../../reducers/general";
import { ThunkResult } from "../..";

// write your general actions here

// this is an example for an action
export const init =
  (): ThunkResult<GeneralState> => async (dispatch, getState) => {
    dispatch({ type: "UPDATE_GENERAL", payload: { message: "init created!" } });
  };

redux/reducers/general/index.ts

import { Action } from "../..";

export interface GeneralState {
  message: string;
}

export const general = (
  state: GeneralState = {
    message: "",
  },
  action: Action<GeneralState>
) => {
  switch (action.type) {
    case "UPDATE_GENERAL":
      return { ...state, ...action.payload };
    default:
      return state;
  }
};

redux/reducers/index.ts

import { combineReducers } from "redux";
import { general } from "./general";

export const mainReducer = combineReducers({
  general,
});

1 - To create a reducer, you must have a redux implementation then run:

rhc create --reducer <reducer-name>

Example

rhc create --reducer auth

src/redux/reducers/auth/index.js

const initialState = {};

export const auth = (state = initialState, action) => {
  switch (action.type) {
    default:
      return state;
  }
};

src/redux/reducers/index.js

import { combineReducers } from "redux";
import { auth } from "./auth";
import { general } from "./general";

export const mainReducer = combineReducers({
  auth,
  general,
});
Redux implementation does not exist
rhc create --reducer <reducer-name> -o

2 - To create multiple reducers, you must have a redux implementation then run:

rhc create --reducer <reducer-name-1> <reducer-name-2> ...
rhc create --action <reducer-name> <action-name>

Example

rhc create --action auth login

src/redux/actions/auth/login.js

export const loginAction = () => async (dispatch, getState) => {
  dispatch({ type: "AUTH_LOGIN", payload: {} });
};

src/redux/actions/auth/index.js

export { loginAction } from "./login";

2 - To create multiple actions, you must have a redux implementation and existed reducer, then run:

rhc create --action <reducer-name> <action-name-1> <action-name-2> ...
./src/redux/reducers/x does not exist

src/redux/index.ts

import { applyMiddleware, compose, createStore } from "redux";
import thunk, { ThunkAction, ThunkDispatch } from "redux-thunk";

import { mainReducer } from "./reducers";

/**
 * the main redux state, with all the reducers
 */
export const mainStore = createStore(
  mainReducer,
  compose(applyMiddleware(thunk))
);

/**
 * Creates a new redux state each time this function is called, this is used only for unit tests, to ensure that we have fresh state on each individual test
 */
export const createMainStore = () => {
  return createStore(mainReducer, compose(applyMiddleware(thunk)));
};

export type StateInterface = ReturnType<typeof mainStore.getState>;

/**
 * list of action types
 */
export type ActionType = "AUTH_LOGIN" | "UPDATE_GENERAL";

export interface Action<T> {
  type: ActionType;
  payload: Partial<T>;
}
export type ThunkResult<
  A = Record<string, unknown>,
  E = Record<string, unknown>
> = ThunkAction<void, StateInterface, E, Action<A>>;

export type Dispatch<A> = ThunkDispatch<
  StateInterface,
  Record<string, unknown>,
  Action<A>
>;

Configuration

With the above steps, you can now create a configuration file which will be used by rhc to create your files with your custom config.

rhc create --config
{
  "withCSS": true,
  "withFunctions": true,
  "withProps": true,
  "defaultExports": true,
  "componentsRoot": "./src/components",
  "pagesRoot": "./src/pages",
  "reduxRoot": "./src/redux",
  "applyReduxThunk": true
}
  1. withCSS: if true, create styles.css file for components and pages, if false, don’t create styles.css file, default is true.
  2. withFunctions: if true, create functions folder for pages, if false, don’t create functions folder, default is true.
  3. withProps: if true, create props interface for components and pages (in TS only), if false, don’t create props interface, default is true.
  4. defaultExports: if true, create default export for components and pages, if false, create named export for components and pages, default is true.
  5. componentsRoot: the root folder for components, default is ./src/components.
  6. pagesRoot: the root folder for pages, default is ./src/pages.
  7. reduxRoot: the root folder for redux, default is ./src/redux.
  8. applyReduxThunk: if true, apply redux-thunk middleware to the store, if false, don’t apply redux-thunk middleware, default is true.