Skip to content

Instantly share code, notes, and snippets.

@pash90
Last active November 15, 2018 02:50
Show Gist options
  • Save pash90/80e7b2bb09f667375cf363a7e3ab7f63 to your computer and use it in GitHub Desktop.
Save pash90/80e7b2bb09f667375cf363a7e3ab7f63 to your computer and use it in GitHub Desktop.
CRA + TS with Redux, RR4, DevTools and History

Introduction

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

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

Step - 1 : Create the base App

npx create-react-app <FOLDER_TO_CREATE_IN> --typescript

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

yarn add redux react-redux react-router connected-react-router history redux-devtools-extension redux-form
yarn add --dev @types/history @types/redux-form @types/react-router @types/react-redux

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

/** Libraries */
import { createStore, applyMiddleware } from 'redux';
import { routerMiddleware } from 'connected-react-router';
import createBrowserHistory from 'history/createBrowserHistory';
import { composeWithDevTools } from 'redux-devtools-extension';

/** Interfaces */
import { AppState, Action } from '../types';

/** Reducers */
import createRootReducer from '../reducers';

const history = createBrowserHistory();

const store = createStore<AppState, Action, {}, {}>(
	createRootReducer(history),
	{},
	composeWithDevTools(applyMiddleware(routerMiddleware(history)))
);

export default store;
export { history };

Step - 4 : Modify index.tsx

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

/** Libraries */
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { ConnectedRouter } from 'connected-react-router';

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

/** Helpers */
import store, { history } from './store';

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

ReactDOM.render(
	<Provider store={store}>
		<ConnectedRouter history={history}>
			<App />
		</ConnectedRouter>
	</Provider>,
document.getElementById('root')
);

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 { connectRouter } from 'connected-react-router';
import { History } from 'history';
import { reducer } from 'redux-form';

export default (history: History) =>
	combineReducers({
		router: connectRouter(history),
		form: reducer
	});

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