Create Redux
- To create a redux implementation run:
rnhc 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 firstrnhc
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
Create a reducer
- To create a reducer, you must have a redux implementation then run:
rnhc create --reducer <reducer-name>
Example
rnhc 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
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> -o
Create multiple reducers
- 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.js
file under thereducers
folder to use the reducers you created.
Actions
Create an action
- 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>
Example
- In this example we are going to create an action for the
auth
reducer, so we will run:
rnhc 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";
Create multiple actions
- 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
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 inrnhc.config.json
file, this will let you create your store and actions without applying theredux-thunk
middleware (For more details check configuration section).