- Clone repository.
- Open command line in the project folder.
- Install packages
yarn
ornpm i
. - Start development server
yarn start
ornpm start
.
yarn start
Start development server.yarn android
Build a standalone APK for Android.yarn ios
Build a standalone IPA for iOS.yarn lint
Check code quality.
/assets
/src
core
/app
/common
/feature1
/feature2
/feature3
shared
index.ts
app.json
package.json
Put static resources like image, video, font and... in this folder.
Usage example: Adding an image
App configurations that will be used by Expo (name, icon, splash image...).
See properties.
Project scripts and dependencies go here.
Use Yarn package manager instead of NPM.
Install a package yarn add <pkg_name>
.
Remove a package yarn remove <pkg_name>
.
This is the entry point of project.
It can be set in package.json
:
{
"main": "src/index.ts"
}
This folder contains root component and configurations...
Creates and configures a Redux store with middlewares (like Saga).
Refer to code for more details...
Root reducer of the application.
Add all the other reducers in here to combine them...
Root saga of the application.
Fork all the other sagas in here to run them in parallel...
Basic types and interfaces for using with redux states.
There are some basic models for async action and error handling which you can use for features state.
Refer to code for more details...
App start up hook that runs initial actions and notifies when ready...
Refer to code for more details...
App global theme and styles.
Refer to code for more details...
Application root component and screen.
It does:
- Add Redux provider.
- Add Appearance provider (for color scheme detection).
- Make app ready.
- Add SafeArea provider.
- Add app theme provider.
- Add Paper provider (the UI kit) and configure its theme.
- Add Navigation container and configure its theme.
- Change status bar style.
- Configure navigators and screens.
Be aware that hooks, components and apis are only available in their providers. For example you can't use Paper components outside the
PaperProvider
.
Use stack navigator for screens that need a header.
Create separate navigator instances for screens that are related to each other.
If you have a code that will be used everywhere in the project, add it here...
Basic http client that can be used for calling back-end apis.
Refer to code for more details...
Simple utility functions that will be used all across the app.
Refer to code for more details...
Every part of the application should be added to the project as a separate module or feature, like Preferences, I18n, Authentication and...
Keep everything that is related to a feature in its own folder as much as possible, to make it portable and easy to copy.
Use kebab-case for file naming (lower case separated by dash).
Features can have different parts, for adding a new feature,
create a folder with the feature name in src
and add these files:
Put every type and interface that related to the feature in here.
If you have a model which has a data with multiple actions, in its interface add a property for the data, and a property for each action with
AsyncState<void>
type that has no data and only keeps status and error. Seeauth.model.ts
for example.
Example home.model.ts
:
// Imports...
export type Status = 'pending' | 'published';
export interface Post {
id: number;
name: string;
body: string;
status: Status;
}
export interface Home {
posts: AsyncState<Post[]>;
}
Define feature actions in here.
Redux states are readonly data, to change their values you have to define actions.
Example home.action.ts
:
// Imports...
export const homePostsRequest = createAction('home/posts/request');
export const homePostsSuccess = createAction<Post[]>('home/posts/success');
export const homePostsFailure = createAction('home/posts/failure', failure());
Reducer is a pure function (with no async calls) that manipulates state value according to actions.
After defining the reducer, add it to
/app/app.reducer.ts
.
Redux Toolkit configures Immer library under the hood, so you can directly mutate (manipulate) states in the reducer which basically is not allowed in Redux.
Example home.reducer.ts
:
// Imports...
const initialState: Home = {
posts: {}
};
export default createReducer(initialState, {
[homePostsRequest.type]: state => {
state.posts.status = 'request';
state.posts.error = null;
},
[homePostsSuccess.type]: (state, action: PayloadAction<Post[]>) => {
state.posts.status = 'success';
state.posts.data = action.payload;
},
[homePostsFailure.type]: (state, action: FailureAction) => {
state.posts.status = 'failure';
state.posts.error = action.error;
}
});
Selectors are simple functions that take the root state and give specific parts.
They can be used in components or sagas.
If the selection has a heavy flow use reselect library to cache the data, because selectors will be called in every ui render and may slow the app.
Example home.selector.ts
:
// Imports...
export const selectHome = selector(state => state.home)();
export const selectHomePosts = selector(state => state.home.posts)();
Put your logics like calling device apis, working with storage, fetching data from the server and... in here.
Use
async/await
for your functions.
Example home.api.ts
:
// Imports...
// Defin constant values here...
export async function getPosts(): Promise<Post[]> {
let {data} = await client.get<Post[]>('/posts');
return data;
}
Saga is a generator function that watches async redux actions and runs some tasks in the background to handle them.
After defining the saga, fork it in
/app/app.saga.ts
.
Refer to Saga document for more information...
Example home.saga.ts
:
// Imports...
export default function* (): SagaIterator {
yield takeLatest(homePostsRequest, handlePosts);
}
function* handlePosts(): SagaIterator {
try {
let posts: Post[] = yield call(getPosts);
yield put(homePostsSuccess(posts));
} catch (err) {
yield put(homePostsFailure(err));
}
}
Application views.
Use Tailwind for styling your components.
Remember that React function components will be executed entirely in every ui rendering. So if there is a heavy computation in the component, cache them by
useMemo()
hook.
Use
.screen.ts
extension for navigation components.
Get theme context by
useTheme()
hook from Paper.
Always use
function
keyword for declaring handlers.
Example home.component.ts
:
// Imports...
// Define constant values here...
// Add cutom styles here...
// Put your styles in a seperate `*.style.ts` file if it got larger.
const styles = StyleSheet.create({
// ...
});
export default function HomeScreen(): ReactElement {
const dispatch = useDispatch();
const home = useSelector(selectHome);
// Other hooks...
// `useState()` hooks...
// Variables...
// Prefer `const` over `let`
const posts = home.posts;
// Effects...
useEffect(() => {
// ...
dispatch(homePostsRequest());
// ...
}, []);
// Event handlers...
function onEvent(...args: any[]): void {
// ...
// Dispatch actions...
// ...
}
// Show posts...
return (
<View/>
);
}
// Write local components, types, interfaces and utility functions here...
Some default features have been added to the project that can be used.
This module contains states, actions and views for persisting user preferences (like theme).
Refer to code for more details...
This module brings localization to the app.
Add messages in /i18n/res
folder, then you can get translation in your components like this:
const {t} = useTranslation();
// ...
<Text>{t('key')}</Text>
Its powered by i18next.
Refer to code for more details...
This module persists navigation history in the development mode to use it in the app reloads.
See Navigation State Persistence.
Refer to code for more details...
This module contains states, actions and views for user authentication.
Refer to code for more details...
email:
[email protected]
password:
nilasoft12345678
###sentry There is a sentry account with that gmail
Don't invent the wheel! Before you write a code, search for similar cases, maybe there is a library for your needs. Libraries like Lodash have a lot of functions that make your life easier.
Since we are using Typescript, please don't leave any type unknown. Try to explicitly specify types for variables, function params, return types and...
// Not good
function name(param) { /* */ }
// Good
function name(param: string | number | any): void | string | number | any { /* */ }
Use IIFE approach for calling async functions in sync scopes:
// None async scope
(async () => {
// Use `await`...
})();
// None async scope
Use
Ctrl+Alt+O
in WebStorm and VSCode to sort imports.
Keep every thing simple and clean, thank you ;)
Expo React Native build tools.
React Native Paper Default UI Kit.
React Navigation For routing and navigation.
Redux State management.
Redux Toolkit Redux helper functions.
Redux Saga Redux side effects (async actions).
Formik Form builder.
Typography Standard styles for texts.
Axois HTTP client.
Lodash Utility functions.
React Native TailwindCSS A react-native styling system, based on TailwindCSS.
ESLint Find and fix problems in your JavaScript code.
Lint-staged Run linters on git staged files.