After completing this guide, you'll have a new project with the following:
- React v16
- Redux
- Redux Form
- React-Router v4
npx create-react-app <FOLDER_TO_CREATE_IN> --typescript
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
/** 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 };
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')
);
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.
- 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"
}
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.