Table of Contents

React

References

Starter commands

yarn create react-app projectName --template typescript
yarn add node-sass react-router-dom @types/react-router-dom @types/react-dom

.htaccess

<IfModule mod_rewrite.c>
    RewriteEngine On
    RewriteBase /
    RewriteRule ^index\.html$ - [L]
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteRule . /index.html [L]
 </IfModule>

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) => <div>
  <h2>{ title }</h2>
  <p>
    { paragraph }
  </p>
</div>
 
const el = <Card title="Welcome!" paragraph="To this example" onClick={() => parentFunc(title)} />

Redux

Redux store implementation which appeals to me these days.

index.ts
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__(),
);
actions.ts
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;
reducers.ts
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<StoreState, Actions> = (
  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;
  }
};
selectors.ts
import { StoreState } from '.';
 
export type StoreSelector<T> = (state: StoreState) => T;
 
export const selectBill: StoreSelector<number> = state => state.bill;
 
export const selectPercentage: StoreSelector<number> = state =>
  state.percentage;
 
export const selectSplit: StoreSelector<number> = state => state.split;
 
export const selectTotal: StoreSelector<string> = state => {
  const total = state.bill + state.bill * (state.percentage / 100);
  return total.toFixed(2);
};
 
export const selectTip: StoreSelector<string> = state => {
  const tip = state.bill * (state.percentage / 100);
  return tip.toFixed(2);
};
 
export const selectPerPerson: StoreSelector<string> = state => {
  const perPerson =
    (state.bill + state.bill * (state.percentage / 100)) / state.split;
  return perPerson.toFixed(2);
};
component.ts
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 (
     <div>
     {split}
     <button onClick={
     dispatch({
              type: ActionTypes.BillChange,
              payload: 10,
            })
      }>
      Act</button>
      </div>
  )
}

Useful libraries