Skip to content

Instantly share code, notes, and snippets.

@mushfiqweb
Forked from pash90/NewCreateReactAppTS.md
Last active September 5, 2018 12:04
Show Gist options
  • Save mushfiqweb/912195643a032f4c57208343fd0feda8 to your computer and use it in GitHub Desktop.
Save mushfiqweb/912195643a032f4c57208343fd0feda8 to your computer and use it in GitHub Desktop.
CRA-Persistor

Introduction

After completing this guide, you'll have a new project with the following:

  • React v16
  • Redux
  • Redux Form
  • React-Router v4
  • LocalStorage persistance

Step - 1 : Create the base App

npx create-react-app <FOLDER_TO_CREATE_IN> --scripts-version=react-scripts-ts

Step - 2 : Add Redux, React Router, History & Redux Form

npm i -S redux react-redux react-router-dom react-router-redux@next history redux-devtools-extension redux-form redux-persist
npm i -D @types/history @types/redux-form @types/react-router-dom @types/react-router-redux @types/react-redux

Step - 3 : Create file - store/index.ts

/** Libraries */
import { createStore, applyMiddleware } from 'redux';
import { routerMiddleware } from 'react-router-redux';
import createBrowserHistory from 'history/createBrowserHistory';
import { composeWithDevTools } from 'redux-devtools-extension';
import { persistStore, persistReducer } from 'redux-persist'; // <-- For persisting state in local storage
import storage from 'redux-persist/lib/storage'; // <-- For persisting state in local storage

/** Interfaces */
// This is the interface for your App. If you do not want to deal with that,
// you can replace `AppState` with `any`
// import { AppState } from "../types/";  <-- Import your AppState here
interface AppState {}

/** Components */
import reducers from '../reducers';

/** Initialisation */
const persistConfig = {
	key: 'root',
	whitelist: ['Account'], // <-- only Account state will persist across browser refreshes
	storage,
};

const persistedReducer = persistReducer(persistConfig, reducers);

// Creating browser history
const history = createBrowserHistory();

// Building the middleware for intercepting and dispatching navigation actions
const middleware = routerMiddleware(history);

// Apply middleware and dev tools
const enhancers = composeWithDevTools(applyMiddleware(middleware));

const store = createStore<AppState>(persistedReducer, enhancers);
const persistor = persistStore(store);

export default store;
export { history, persistor };

Step - 4 : Modify index.tsx

Modify your index.tsx file to include the store and history we just created above

/** Libraries */
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { ConnectedRouter } from 'react-router-redux';
import { Route } from 'react-router-dom';
import { PersistGate } from 'redux-persist/integration/react';

/** Components */
import App from './components/App';
import store, { history, persistor } from './store/';

/** Styles */
import './index.css';

ReactDOM.render(
	<Provider store={store}>
		<PersistGate loading={null} persistor={persistor}>
			<ConnectedRouter history={history}>
				<Route path="/" component={App} />
			</ConnectedRouter>
		</PersistGate>
	</Provider>,
	document.getElementById('root') as HTMLElement
);

Step - 5 : Reducers

Apart from your component reducers, we also need to add reducers for redux-form and react-router

/** Libraries */
import { combineReducers } from 'redux';
import { routerReducer } from 'react-router-redux';
import { reducer as formReducer } from 'redux-form';

/** Components */
// Import your component reducers here

/** Intefaces */
// import { AppState } from '../types'; <-- Import your AppState here
interface AppState {}

/**
 * This is actual structure of your App's redux store.
 * The keys in the object below is the keys you'll use
 * to access it later in your components. You can add
 * as many as you like in the format:
 * [componentKey]:[componentReducer]
 *
 */
export default combineReducers<AppState>({
	// redux-form && react-router
	router: routerReducer,
	form: formReducer,

	// component reducers
});

I like to specify an initial state for all my components. That way I have never have to worried about something not being defined at any point.

/**
 * Helpers
 *
 * Since we not using ImmutableJS, we need to deep clone
 * every time we need to make a change
 */
const deepClone = (objectToClone: object): FounderState =>
	JSON.parse(JSON.stringify(objectToClone));

/** Initial State */
// Define your State interface and then the initial state
const initialState: {} = {};

export const componentReducer = (
	state: ComponentState = initialState,
	action: ActionInterface
): ComponentState => {
	// Handle your actions here
	// using deep clone like this :
	switch (action.type) {
		case 'SOME_ACTION_TYPE': {
			//...handle action
			const newState = deepClone(state);
			newState.someProp = action.payload.someOtherProp;

			return newState;
		}

		default:
			return state;
	}
};

Implementing actions should be faily straight forward at this point. Every different action type you write, it needs to be handled here.

Step - 6 : Add a Preprocessor - SASS

  • Add SASS
npm install --save node-sass-chokidar
  • Add the following lines to scripts in package.json
   "scripts": {
+    "build-css": "node-sass-chokidar --include-path ./src --include-path ./node_modules src/ -o src/",
+    "watch-css": "npm run build-css && node-sass-chokidar --include-path ./src --include-path ./node_modules src/ -o src/ --watch --recursive",
     "start": "react-scripts start",
     "build": "react-scripts build",
     "test": "react-scripts test --env=jsdom",
  • We need to watch the SASS files locally. No way to run two processes in parallel(yet) so we use this package
npm install --save npm-run-all
  • Modify the following scripts in package.json
"scripts": {
     "build-css": "node-sass-chokidar --include-path ./src --include-path ./node_modules src/ -o src/",
     "watch-css": "npm run build-css && node-sass-chokidar --include-path ./src --include-path ./node_modules src/ -o src/ --watch --recursive",
-    "start": "react-scripts-ts start",
-    "build": "react-scripts-ts build",
+    "start-js": "react-scripts-ts start",
+    "start": "npm-run-all -p watch-css start-js",
+    "build-js": "react-scripts-ts build",
+    "build": "npm-run-all build-css build-js",
     "test": "react-scripts-ts test --env=jsdom",
     "eject": "react-scripts-ts eject"
   }

Step - 7 : Almost there ...

Once you've completed the above steps (including creating the reducers and actions as well), you are ready to run your project. Just execute npm start to start a local server OR npm run build to create a production build.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment