rnhc provide you more flexibility and easy to use approach to create your files and folders for your componets, screens navigations and redux implementation.
-
By default
rnhccreate 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
--jsor--tsoptions at the end of yourcreatecommands. -
rnhcwill not overwrite the existed implementation for all of thecreatecommands. -
If you want to overwrite the existed implementation for a specific
createcommand you can add the--overwriteor-ooption at the end of the command.
The following points shows how to use the create command.
- To create your components simply run:
rnhc create -c <component-name>- This will create a component named after the given name
<component-name>under thesrc/components/<component-name>/folder.
rnhc create -c TestComponent- This command will create the following directory
src/components/test-component/:
src/
└── components
└── test-component
├── __tests__
│ └── index.spec.jsx
├── index.jsx
└── styles.js- Where
index.jsxrepresents the React FC component that contains the following:
import { Text, View } from "react-native";
import { TestComponentStyles } from "./styles";
const TestComponent = () => {
return (
<View>
<Text>TestComponent component created!</Text>
</View>
);
};
export default TestComponent;- And for the
styles.jsyou will see:
import { StyleSheet } from "react-native";
export const TestComponentStyles = StyleSheet.create({});- To create multiple components simply run:
rnhc create -c <component-name-1> <component-name-2> ...- This will create multiple components for the given names under the
src/components/folder.
rnhc create -c comp-1 comp-2- This command will create under the
src/components/folder the following:
src/
└── components
├── comp-1
│ ├── __tests__
│ │ └── index.spec.jsx
│ ├── index.jsx
│ └── styles.js
└── comp-2
├── __tests__
│ └── index.spec.jsx
├── index.jsx
└── styles.js- To create one or mutliple components in a specified path that resides under the
src/components/folder, simply run:
rnhc create -c <component-name-1> <component-name-2> ... -f <folder-path>- This will create your components under the
src/components/<folder-path>/folder.
rnhc 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
│ ├── __tests__
│ │ └── index.spec.jsx
│ ├── index.jsx
│ └── styles.js
└── comp-2
├── __tests__
│ └── index.spec.jsx
├── index.jsx
└── styles.jsYou can also create your components in atom design pattern, which is a design pattern that is used to create small and reusable components.
-
To create your atoms simply run:
rnhc create -c <atom-name> --atom- This will create an atom named after the given name
<atom-name>under thesrc/components/atoms/<atom-name>/folder.
rnhc create -c TestAtom --atom- This command will create the following directory
src/components/atoms/test-atom/:
src/
└── components
└── atoms
└── test-atom
├── __tests__
│ └── index.spec.jsx
├── index.jsx
└── styles.js-
To create your molecules simply run:
rnhc create -c <molecule-name> --molecule- This will create a molecule named after the given name
<molecule-name>under thesrc/components/molecules/<molecule-name>/folder.
rnhc create -c TestMolecule --molecule- This command will create the following directory
src/components/molecules/test-molecule/:
src/
└── components
└── molecules
└── test-molecule
├── __tests__
│ └── index.spec.jsx
├── index.jsx
└── styles.js-
To create your organisms simply run:
rnhc create -c <organism-name> --organism- This will create an organism named after the given name
<organism-name>under thesrc/components/organisms/<organism-name>/folder.
rnhc create -c TestOrganism --organism- This command will create the following directory
src/components/organisms/test-organism/:
src/
└── components
└── organisms
└── test-organism
├── __tests__
│ └── index.spec.jsx
├── index.jsx
└── styles.js- You can also create your components in a specified path that resides under the
src/components/folder, simply run:
rnhc create -c <component-name-1> <component-name-2> ... -f <folder-path> --atom-
This will create your components under the
src/components/<folder-path>/atoms/folder. -
The same goes for molecules and organisms.
- To create your screen simply run:
rnhc create -s <screen-name>- This will create a screen after the given name
screen-nameunder thesrc/screens/folder.
rnhc create -s testScreen- This will create the following:
src/
└── screens
└── test-screen
├── __tests__
│ └── index.spec.jsx
├── functions
│ └── index.js
├── index.jsx
└── styles.js- Where
index.jsxrepresent the screen which is nothing but a React FC component that contains the following:
import { Text, View } from "react-native";
import {} from "./functions";
import { TestScreenStyles } from "./styles";
const TestScreenScreen = () => {
return (
<View>
<Text>TestScreen screen created!</Text>
</View>
);
};
export default TestScreenScreen;- As for
styles.jsyou will find:
import { StyleSheet } from "react-native";
export const TestScreenStyles = StyleSheet.create({});- And under the
functionsfolder you should write your screen's functions and export them infunction/index.jsfile, which by default it will contain the following:
// write your TestScreen screen functions here- To create multiple screens simply run:
rnhc create -s <screen-name-1> <screen-name-2> ...- This will create multiple screens under the
src/screens/folder.
rnhc create -s screen-1 screen-2- This will create the following:
src/
└── screens
├── screen-1
│ ├── __tests__
│ │ └── index.spec.jsx
│ ├── functions
│ │ └── index.js
│ ├── index.jsx
│ └── styles.js
└── screen-2
├── __tests__
│ └── index.spec.jsx
├── functions
│ └── index.js
├── index.jsx
└── styles.js- To create one or multiple screens in a specific path that resides under
src/screens/folder, simply run:
rnhc create -s <screen-name-1> <screen-name-2> ... -f <folder-path>- This will create your screens under the
src/screens/<folder-path>folder.
rnhc create -s screen-1 screen-2 -f foo/bar- This will create the following:
src/
└── screens
└── foo
└── bar
├── screen-1
│ ├── __tests__
│ │ └── index.spec.jsx
│ ├── functions
│ │ └── index.js
│ ├── index.jsx
│ └── styles.js
└── screen-2
├── __tests__
│ └── index.spec.jsx
├── functions
│ └── index.js
├── index.jsx
└── styles.jsNavigations are the relations between chosen screens, those relations can be stack navigation, drawer navigation or tab navigation.
- To create a navigation between two or more screens, simply run:
rnhc create -n <navigation-type> <screen-name-1> <screen-name-2> ...-
The
<navigation-type>can be either of these types:stack,native-stack,drawer,bottom-tabs,material-bottom-tabsormaterial-top-tabs. -
The number of given screens should be 2 or more.
-
If one of the screens does not exist,
rnhcwill try to create thenavigation.jsxfile that resides next to the existed screens. -
If all screens does not exist,
rhncwill prompt you the following:
None of these screens existrnhc create -n stack screen-1 screen-2- This will create a
navigation.jsxfile next toscreen-1andscreen-2screens as the following:
src/
└── screens
├── navigation.jsx
├── screen-1
│ ├── __tests__
│ │ └── index.spec.jsx
│ ├── functions
│ │ └── index.js
│ ├── index.jsx
│ └── styles.js
└── screen-2
├── __tests__
│ └── index.spec.jsx
├── functions
│ └── index.js
├── index.jsx
└── styles.js- The content for the
navigation.jsxfile is as the following:
import { createStackNavigator } from "@react-navigation/stack";
const { Navigator, Screen } = createStackNavigator();
import Screen1Screen from "./screen-1";
import Screen2Screen from "./screen-2";
const routes = [
{
name: "screen-1",
component: Screen1Screen,
},
{
name: "screen-2",
component: Screen2Screen,
},
];
const Navigation = () => {
return (
<Navigator>
{routes.map(({ name, component }) => (
<Screen key={name} name={name} component={component} />
))}
</Navigator>
);
};
export default Navigation;- To create a navigation file for screens that resides in a specific path under the
src/screens/folder, you can run this:
rnhc create -n <navigation-type> <screen-name-1> <screen-name-2> ... -f <folder-path>- This will create the
navigation.jsxfile under thesrc/screens/<folder-path>/folder for the screens that resides in the same location.
rhnc create -n drawer screen-1 screen-2 -f foo/bar- This will create
navigation.jsxfile undersrc/screens/foo/bar/folder which contains the following:
import { createStackNavigator } from "@react-navigation/stack";
const { Navigator, Screen } = createStackNavigator();
import Screen1Screen from "./screen-1";
import Screen2Screen from "./screen-2";
const routes = [
{
name: "screen-1",
component: Screen1Screen,
},
{
name: "screen-2",
component: Screen2Screen,
},
];
const Navigation = () => {
return (
<Navigator>
{routes.map(({ name, component }) => (
<Screen key={name} name={name} component={component} />
))}
</Navigator>
);
};
export default Navigation;- To create a navigation that depends on another navigation you can run the same command as this:
rnhc create -n <navigation-type> <screen-name-1> <screen-name-2> ... <another-navigation-folder>- This will create
navigation.jsxfile that resides in the same location for the given inputs.
In this example, we have the following structure:
src/
└── screens
├── folder
│ ├── navigation.jsx
│ ├── screen-one
│ │ ├── __tests__
│ │ │ └── index.spec.jsx
│ │ ├── functions
│ │ │ └── index.js
│ │ ├── index.jsx
│ │ └── styles.js
│ └── screen-two
│ ├── __tests__
│ │ └── index.spec.jsx
│ ├── functions
│ │ └── index.js
│ ├── index.jsx
│ └── styles.js
└── screen-three
├── __tests__
│ └── index.spec.jsx
├── functions
│ └── index.js
├── index.jsx
└── styles.jsWhere we have a drawer navigation between screen-one and screen-two under the folder folder. And we want to create a stack navigation between folder and screen-three. And to show that rnhc will continue to run for the existed screens we added in the command line screen-four which does not exist as the following:
rnhc create -n stack folder screen-three screen-four- The command will output the following:
src/screens/screen-four/ does not exist
src/screens/navigation.jsx created- And it will add a new
navigation.jsxthat resides betweenfolderandscreen-threeas the following:
src/
└── screens
├── folder
│ ├── navigation.jsx
│ ├── screen-one
│ │ ├── __tests__
│ │ │ └── index.spec.jsx
│ │ ├── functions
│ │ │ └── index.js
│ │ ├── index.jsx
│ │ └── styles.js
│ └── screen-two
│ ├── __tests__
│ │ └── index.spec.jsx
│ ├── functions
│ │ └── index.js
│ ├── index.jsx
│ └── styles.js
├── navigation.jsx
└── screen-three
├── __tests__
│ └── index.spec.jsx
├── functions
│ └── index.js
├── index.jsx
└── styles.js- And the content of the new
navigation.jsxfile will be like this:
import { createStackNavigator } from "@react-navigation/stack";
const { Navigator, Screen } = createStackNavigator();
import FolderNavigation from "./folder/navigation";
import ScreenThreeScreen from "./screen-three";
const routes = [
{
name: "folder",
component: FolderNavigation,
},
{
name: "screen-three",
component: ScreenThreeScreen,
},
];
const Navigation = () => {
return (
<Navigator>
{routes.map(({ name, component }) => (
<Screen key={name} name={name} component={component} />
))}
</Navigator>
);
};
export default Navigation;- To create a navigation file for multiple screens that resides at the root of the
src/screens/folder, you can run this:
rnhc create -n <navigation-type>- This will create the navigation file for all existed screens in the
src/screens/folder.
You can also run this command to create a navigation file for multiple screens that resides in a specific path under the src/screens/ folder:
rnhc create -n <navigation-type> -f <folder-path>-
This will create the navigation file for all existed screens in the
src/screens/<folder-path>/folder. -
This also work for the nested navigations.
-
All the sub folders should contain the navigation files so it can be added to the navigation file you want to create, for example take this structure:
src/
└── screens
├── folder
│ ├── screen-one
│ │ ├── __tests__
│ │ │ └── index.spec.jsx
│ │ ├── functions
│ │ │ └── index.js
│ │ ├── index.jsx
│ │ └── styles.js
│ └── screen-two
│ ├── __tests__
│ │ └── index.spec.jsx
│ ├── functions
│ │ └── index.js
│ ├── index.jsx
│ └── styles.js
├── navigation.jsx
├── screen-four
│ ├── __tests__
│ │ └── index.spec.jsx
│ ├── functions
│ │ └── index.js
│ ├── index.jsx
│ └── styles.js
└── screen-three
├── __tests__
│ └── index.spec.jsx
├── functions
│ └── index.js
├── index.jsx
└── styles.jsWhen you try to create a navigation like this:
rnhc create -n stackIt will output the following:
./src/screens/folder/navigation.jsx does not exist
./src/screens/navigation.jsx createdAnd the ./src/screens/navigation.jsx file will contain only screen-three and screen-four because the src/screens/folder does not contain a navigation file.
So if you want to create a navigation file for all existed screens in the src/screens/ folder, you must take in consideration that all subfolders must contain a navigation file first and then you can either update the navigation file or create a new one.
By updating it means overwriting in other words, so you can just do this:
rnhc create -n stack -oYou can create your screens and components with your defined templates by following these steps:
-
First thing to do is to create a
.templatefolder at the root of your react project. -
Inside the
.templatefolder you can add your template, for examplecomponentWithUseEffect.tsx(the file extension doesn't matter so it could be*.jsx,*.jsor*.tsx):
import { useEffect } from "react";
import { View, Text } from "react-native";
export default function __COMPONENT__() {
useEffect(() => {}, []);
return (
<View>
<Text>Hello, World!</Text>
</View>
);
}-
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 screens as the following:
rnhc create -c <component-name> -t <template-name>rnhc create -s <screen-name> -t <template-name>- And of course, you can create multiple components or screens with the same template.
As for our example it can be used like this for the above template:
rnhc create -c comp -t componentWithUseEffect-
This will create
compcomponent undersrc/components/folder and theindex.jsxfor this component will contain the same code written in the template. -
For the screen case, the
index.jsxfor that screen will contain the code written in the template.
- To create a redux implementation run:
rnhc create -r- This will create a
reduxfolder under thesrc/folder containing the following:
src
└───redux
│ index.js
│
├───actions
│ └───general
│ index.js
│
└───reducers
│ index.js
│
└───general
index.js- Where
index.jsunder thereduxfolder 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
actionsfolder contains the action for each reducer, as for a example, at firstrnhcwill create a sample reducer and action which is calledgeneral. -
The
generalaction'sindex.jscontains:
// 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
generalreducer'sindex.jscontains:
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.jsfile under thereducersfolder 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,
});1 - To create a reducer, you must have a redux implementation then run:
rnhc create --reducer <reducer-name>rnhc create --reducer auth- This will create a
authreducer under thesrc/redux/reducers/folder and theindex.jsfor 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.jsfile under thereducersfolder to use it in thecombineReducersfunction.
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
rnhc create -r, this command will prompt:
Redux implementation does not exist- You can also overwrite the reducer by running:
rnhc create --reducer <reducer-name> -o2 - To create multiple reducers, you must have a redux implementation then run:
rnhc create --reducer <reducer-name-1> <reducer-name-2> ...-
This will also update your
index.jsfile under thereducersfolder to use the reducers you created. -
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:
rnhc create --action <reducer-name> <action-name>- In this example we are going to create an action for the
authreducer, so we will run:
rnhc create --action auth login- This will create a
loginaction under thesrc/redux/actions/auth/folder and thelogin.jsfor 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.jsfile 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:
rnhc 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
ActionTypein thesrc/redux/index.tsfile. For example if you create an action for theauthreducer, 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-thunkyou can set that inrnhc.config.jsonfile, this will let you create your store and actions without applying theredux-thunkmiddleware (For more details check configuration section).
With the above steps, you can now create a configuration file which will be used by rnhc to create your files with your custom config.
- To create a default configuration file run:
rnhc create --config- This will create a
rnhc.config.jsonfile at the root of your project. The file will contain the following:
{
"withStyles": true,
"withFunctions": true,
"withProps": true,
"withTests": true,
"defaultExports": true,
"componentsRoot": "./src/components",
"screensRoot": "./src/screens",
"reduxRoot": "./src/redux",
"applyReduxThunk": true
}withStyles: if true, createstyles.js(orstyles.ts) file for components and screens, if false, don't createstyles.js(orstyles.ts) file, default is true.withFunctions: if true, createfunctionsfolder for screens, if false, don't createfunctionsfolder, default is true.withProps: if true, create propsinterfacefor components and screens (in TS only), if false, don't create propsinterface, default is true.withTests: if true, create__tests__folder for components and screens, if false, don't create__tests__folder, 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.screensRoot: the root folder for screens, default is./src/screens.reduxRoot: the root folder for redux, default is./src/redux.applyReduxThunk: if true, applyredux-thunkmiddleware to the store, if false, don't applyredux-thunkmiddleware, default is true.
- If no configuration file is found or you don't specify some of the configuration, the default configuration will be used.