yarn create react-app projectName --template typescript
yarn add node-sass react-router-dom @types/react-router-dom @types/react-dom
===== .htaccess =====
RewriteEngine On
RewriteBase /
RewriteRule ^index\.html$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.html [L]
===== Environments =====
Development / production environments can be set as following
interface Env {
ENV_STATUS: string;
API_HOST: string;
}
const development: Env = {
ENV_STATUS: "development",
API_HOST: "localhost:3333"
};
const production: Env = {
ENV_STATUS: "production",
API_HOST: "https://server.paperkeeper.org:3333"
};
const config =
process.env.REACT_APP_ENV === "development" ? development : production;
export const Environment = {
...config
};
To pass the the env variable modify ''package.json'' to
...
"start": "REACT_APP_ENV=production react-scripts start",
"start:dev": "REACT_APP_ENV=development react-scripts start",
"build": "REACT_APP_ENV=production react-scripts build",
...
===== Snippets =====
==== Component props ====
import React from 'react';
type CardProps = {
title: string,
paragraph: string
parentFunc: (s: string) => void;
}
export const Card = ({ title, paragraph }: CardProps) =>
{ title }
{ paragraph }
const el = parentFunc(title)} />
====Redux ====
Redux store implementation which appeals to me these days.
import { createStore, compose } from 'redux';
import { rootReducer } from './reducers';
declare global {
interface Window {
__REDUX_DEVTOOLS_EXTENSION__?: Function;
__REDUX_DEVTOOLS_EXTENSION_COMPOSE__?: typeof compose;
}
}
export interface StoreState {
bill: number;
percentage: number;
split: number;
}
export const store = createStore(
rootReducer,
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__(),
);
import { Action } from 'redux';
export enum ActionTypes {
BillChange = '[Bill] change',
PercentageChange = '[Percentage] change',
SplitIncrement = '[Split] increment',
SplitDecrement = '[Split] decrement',
Reset = '[Reset]',
}
export interface BillChangeAction extends Action {
type: ActionTypes.BillChange;
payload: string;
}
export interface PercentageChangeAction extends Action {
type: ActionTypes.PercentageChange;
payload: string;
}
export interface SplitIncrementAction extends Action {
type: ActionTypes.SplitIncrement;
}
export interface SplitDecrementAction extends Action {
type: ActionTypes.SplitDecrement;
}
export interface ResetAction extends Action {
type: ActionTypes.Reset;
}
export type Actions =
| BillChangeAction
| PercentageChangeAction
| SplitIncrementAction
| SplitDecrementAction
| ResetAction;
import { Reducer } from 'redux';
import { StoreState } from '.';
import { Actions, ActionTypes } from './actions';
export const initalState: StoreState = {
bill: 0,
percentage: 0,
split: 1,
};
export const rootReducer: Reducer = (
state = initalState,
action,
) => {
switch (action.type) {
case ActionTypes.BillChange:
return {
...state,
bill: Number(action.payload),
};
case ActionTypes.PercentageChange:
return {
...state,
percentage: Number(action.payload),
};
case ActionTypes.SplitIncrement:
return {
...state,
split: state.split + 1,
};
case ActionTypes.SplitDecrement:
const split = state.split - 1;
return {
...state,
split: split >= 1 ? split : state.split,
};
case ActionTypes.Reset:
return initalState;
default:
return state;
}
};
import { StoreState } from '.';
export type StoreSelector = (state: StoreState) => T;
export const selectBill: StoreSelector = state => state.bill;
export const selectPercentage: StoreSelector = state =>
state.percentage;
export const selectSplit: StoreSelector = state => state.split;
export const selectTotal: StoreSelector = state => {
const total = state.bill + state.bill * (state.percentage / 100);
return total.toFixed(2);
};
export const selectTip: StoreSelector = state => {
const tip = state.bill * (state.percentage / 100);
return tip.toFixed(2);
};
export const selectPerPerson: StoreSelector = state => {
const perPerson =
(state.bill + state.bill * (state.percentage / 100)) / state.split;
return perPerson.toFixed(2);
};
import * as React from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { selectBill, selectSplit, selectPercentage } from '../store/selectors';
import { ActionTypes } from '../store/actions';
export const MyComponent = () => {
const split = useSelector(selectSplit);
const dispatch = useDispatch();
return (
{split}
)
}
===== Useful libraries =====
* [[https://react-icons.netlify.com|react-icons]]
* [[https://reacttraining.com/react-router/web/guides/quick-start|react-router-dom]]
* [[https://www.npmjs.com/package/react-masonry-css|react-mansory-css]]
* [[https://www.npmjs.com/package/uuid|uuid]]
* [[https://react-hook-form.com/|react-hook-form]]