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.
-
By default
rhc
create files with the project’s language, so if the project is written in TypeScript all the created files will be in TypeScript, and the same applies for JavaScript. -
If you want to force the use of a specific language you can add the
--js
or--ts
options at the end of yourcreate
commands. -
rhc
will not overwrite the existed implementation for all of thecreate
commands.
The following points shows how to use the create
command.
Components
- To create your components simply run:
rhc create -c <component-name>
- This will create a component named after the given name
<component-name>
under thesrc/components/<component-name>/
folder.
Example
rhc create -c test-component
- This command will create the following directory
src/components/test-component/
:
src
└───components
└───test-component
index.jsx
styles.css
- Where
index.jsx
represents the React FC component that contains the following:
import "./styles.css";
const TestComponent = () => {
return <div>TestComponent component created!</div>;
};
export default TestComponent;
- And for the
styles.css
you will see:
/* TestComponent styles */
- To create multiple components simply run:
rhc create -c <component-name-1> <component-name-2> ...
- This will create multiple components for the given names under the
src/components/
folder.
Exmaple
rhc create -c comp-1 comp-2
- This command will create under the
src/components/
folder the following:
src
└───components
├───comp-1
│ index.jsx
│ styles.css
│
└───comp-2
index.jsx
styles.css
- 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>
- This will create your components under the
src/components/<folder-path>/
folder.
Example
rhc create -c comp-1 comp-2 -f foo/bar
- This command will create under the
src/components/
folder the following:
src
└───components
└───foo
└───bar
├───comp-1
│ index.jsx
│ styles.css
│
└───comp-2
index.jsx
styles.css
Pages
- To create your page simply run:
rhc create -p <page-name>
- This will create a page after the given name
page-name
under thesrc/pages/
folder.
Example
rhc create -p test-page
- This will create the following:
src
pages
└───test-page
│ index.jsx
│ styles.css
│
└───functions
index.js
- Where
index.jsx
represent the page which is nothing but a React FC component that contains the following:
import "./styles.css";
const TestPage = () => {
return <div>TestPage page created!</div>;
};
export default TestPage;
- As for
styles.css
you will find:
/* TestPage styles */
- And under the
functions
folder you should write your page’s functions and export them infunction/index.js
file, which by default it will contain the following:
// write your TestPage functions here
- To create multiple pages simply run:
rhc create -p <page-name-1> <page-name-2> ...
- This will create multiple pages under the
src/pages/
folder.
Example
rhc create -p page-1 page-2
- This will create the following:
src
pages
├───page-1
│ │ index.jsx
│ │ styles.css
│ │
│ └───functions
│ index.js
│
└───page-2
│ index.jsx
│ styles.css
│
└───functions
index.js
- 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>
- This will create your pages under the
src/pages/<folder-path>
folder.
Example
rhc create -p page-1 page-2 -f foo/bar
- This will create the following:
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:
-
First thing to do is to create a
.template
folder at the root of your react project. -
Inside the
.template
folder you can add your template, for examplecomponentWithUseEffect.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>
);
}
-
There is a restriction in naming these templates which is you should not put dots (
.
) between the name, like this (component.WithUseEffect.jsx
). It should only contain one dot that makes the extension file like we’re doing above. -
You should type
__COMPONENT__
in the template file and it will be replaced with the component name you want to create.
- 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>
- And of course, you can create multiple components or pages with the same template.
Example
As for our example it can be used like this for the above template:
rhc create -c comp -t componentWithUseEffect
-
This will create
comp
component undersrc/components/
folder and theindex.jsx
for this component will contain the same code written in the template. -
For the page case, the
index.jsx
for that page will contain the code written in the template.
Redux
- To create a redux implementation run:
rhc create -r
- This will create a
redux
folder under thesrc/
folder containing the following:
src
└───redux
│ index.js
│
├───actions
│ └───general
│ index.js
│
└───reducers
│ index.js
│
└───general
index.js
- Where
index.js
under theredux
folder contains the redux store definition:
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)));
};
-
And
actions
folder contains the action for each reducer, as for a example, at firstrhc
will create a sample reducer and action which is calledgeneral
. -
The
general
action’sindex.js
contains:
// 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!" } });
};
- And the
general
reducer’sindex.js
contains:
const initialState = { message: "" };
export const general = (state = initialState, action) => {
switch (action.type) {
case "UPDATE_GENERAL":
return { ...state, ...action.payload };
default:
return state;
}
};
- And the
index.js
file under thereducers
folder contains the following:
import { combineReducers } from "redux";
import { general } from "./general";
export const mainReducer = combineReducers({
general,
});
- In TypeScript, the files will be written as the following:
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,
});
-
Reducers
1 - To create a reducer, you must have a redux implementation then run:
rhc create --reducer <reducer-name>
Example
rhc create --reducer auth
- This will create a
auth
reducer under thesrc/redux/reducers/
folder and theindex.js
for this reducer will contain the same code written in the template.
src/redux/reducers/auth/index.js
const initialState = {};
export const auth = (state = initialState, action) => {
switch (action.type) {
default:
return state;
}
};
- It will also add the reducer to the
index.js
file under thereducers
folder to use it in thecombineReducers
function.
src/redux/reducers/index.js
import { combineReducers } from "redux";
import { auth } from "./auth";
import { general } from "./general";
export const mainReducer = combineReducers({
auth,
general,
});
- If you don’t have a redux implementation create using
rhc create -r
, this command will prompt:
Redux implementation does not exist
- You can also overwrite the reducer by running:
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> ...
-
This will also update your
index.js
file under thereducers
folder to use the reducers you created. -
Actions
- To create an action, you must have a redux implementation as wee as the reducer you want to add an action for it, then run:
rhc create --action <reducer-name> <action-name>
Example
- In this example we are going to create an action for the
auth
reducer, so we will run:
rhc create --action auth login
- This will create a
login
action under thesrc/redux/actions/auth/
folder and thelogin.js
for this action will contain the same code written in the template.
src/redux/actions/auth/login.js
export const loginAction = () => async (dispatch, getState) => {
dispatch({ type: "AUTH_LOGIN", payload: {} });
};
- And it will update the
index.js
file undersrc/redux/actions/auth/
to export the action.
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> ...
- If the reducer doesn’t exist, you will get an error like this:
./src/redux/reducers/x does not exist
- Keep in mind that this also works for TypeScript projects. Even better when creating an action for a reducer in TypeScript, you will get TypeScript support as well as updating the
ActionType
in thesrc/redux/index.ts
file. For example if you create an action for theauth
reducer, you will get the following:
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>
>;
-
It will also update the necessary files that imports and exports modules in order to use the action in the reducer.
-
Also another note, if you prefer not using
redux-thunk
you can set that inrhc.config.json
file, this will let you create your store and actions without applying theredux-thunk
middleware (For more details check configuration section).
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.
- To create a default configuration file run:
rhc create --config
- This will create a
rhc.config.json
file at the root of your project. The file will contain the following:
{
"withCSS": true,
"withFunctions": true,
"withProps": true,
"defaultExports": true,
"componentsRoot": "./src/components",
"pagesRoot": "./src/pages",
"reduxRoot": "./src/redux",
"applyReduxThunk": true
}
withCSS
: if true, createstyles.css
file for components and pages, if false, don’t createstyles.css
file, default is true.withFunctions
: if true, createfunctions
folder for pages, if false, don’t createfunctions
folder, default is true.withProps
: if true, create propsinterface
for components and pages (in TS only), if false, don’t create propsinterface
, default is true.defaultExports
: if true, create default export for components and pages, if false, create named export for components and pages, default is true.componentsRoot
: the root folder for components, default is./src/components
.pagesRoot
: the root folder for pages, default is./src/pages
.reduxRoot
: the root folder for redux, default is./src/redux
.applyReduxThunk
: if true, applyredux-thunk
middleware to the store, if false, don’t applyredux-thunk
middleware, default is true.
- If no configuration file is found or you don’t specify some of the configuration, the default configuration will be used.