yarn create react-app projectName --template typescript yarn add node-sass react-router-dom @types/react-router-dom @types/react-dom
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index\.html$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.html [L]
</IfModule>
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",
...
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 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<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; } };
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); };
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> ) }