# React Flow

# Starting a React project

# Create

When creating React applications we use our custom Three-11 (opens new window) templates. According to our preference to use or not to use TypeScript, you can choose by two type of templates: the JS (opens new window) standard one, or the one with TypeScript (opens new window).
Considering the advantages TypeScript gives us, we recommend to use the template (opens new window) with TypeScript included.

  1. Download the required React template: React template JS standard (opens new window) or React template with TypeScript (opens new window).

  2. Open the project in VS Code, and run yarn / yarn install, to install all the dependencies.

  3. Checkout all the files and make sure you are familiar with the initial project structure and all the pre-configure goodies such as styles, classes, mixins, variables, components, containers, etc.

  4. Great, you are good to go!!!

# When created

  1. Checkout and evaluate the provided design.
  2. Define the components.
  3. Define which components and elements will be reusable.
  4. Define which elements are repeatable and plan their style: text blocks, headings, icons, etc.
  5. For images:
    1. Use SVGs for all icons.
    2. Use PNGs for icons which cannot be exported as SVGs.
    3. Use JPGs for content images (images which are subject to change vithe WPadmin).
  6. Define which fonts are used and if they are provided or available.

# Before Develop

  1. Export needed images in right format from the provided design.
  2. Open package.json file and change the data (name, description..), where needed.
  3. Add all the meta information needed in the <head> of index.html located in the root directory.
  4. Add favicon to the project. All the favicon images should go to assets/images/favicon, and the links for them, in the <head> of index.html file, located at the root directory.
<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="utf-8" />
		<meta name="viewport" content="width=device-width, initial-scale=1" />
		<meta name="mobile-web-app-capable" content="yes" />

		<link rel="apple-touch-icon-precomposed" sizes="57x57" href="/assets/>apple-touch-icon-57x57.png" />
		<link rel="apple-touch-icon-precomposed" sizes="114x114" href="/assets/>apple-touch-icon-114x114.png" />
		<link rel="apple-touch-icon-precomposed" sizes="72x72" href="/assets/>apple-touch-icon-72x72.png" />
		<link rel="apple-touch-icon-precomposed" sizes="144x144" href="/assets/>apple-touch-icon-144x144.png" />
		<link rel="apple-touch-icon-precomposed" sizes="60x60" href="/assets/>apple-touch-icon-60x60.png" />
		<link rel="apple-touch-icon-precomposed" sizes="120x120" href="/assets/>apple-touch-icon-120x120.png" />
		<link rel="apple-touch-icon-precomposed" sizes="76x76" href="/assets/>apple-touch-icon-76x76.png" />
		<link rel="apple-touch-icon-precomposed" sizes="152x152" href="/assets/>apple-touch-icon-152x152.png" />
		<link rel="icon" type="image/png" href="/assets/favicon-196x196.png" />
		sizes="196x196" />
		<link rel="icon" type="image/png" href="/assets/favicon-96x96.png" />
		sizes="96x96" />
		<link rel="icon" type="image/png" href="/assets/favicon-32x32.png" />
		sizes="32x32" />
		<link rel="icon" type="image/png" href="/assets/favicon-16x16.png" />
		sizes="16x16" />
		<link rel="icon" type="image/png" href="/assets/favicon-128.png" />
		sizes="128x128" />

		<meta name="application-name" content="NAME" />
		<meta name="msapplication-TileColor" content="#fff" />
		<meta name="msapplication-TileImage" content="/mstile-144x144.png" />
		<meta name="msapplication-square70x70logo" content="/mstile-70x70.png" />
		<meta name="msapplication-square150x150logo" content="/mstile-150x150.png" />
		<meta name="msapplication-wide310x150logo" content="/mstile-310x150.png" />
		<meta name="msapplication-square310x310logo" content="/mstile-310x310.png" />
		<meta name="theme-color" content="#fff" />

		//Include the font needed
		<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Open>+Sans:400,600,700&display=swap" />
		<link href="//fonts.googleapis.com/css?family=Roboto:300,400,500,700,900" />
		rel="stylesheet" />

		<title>Project title</title>
	</head>
	<body>
		<div id="app"></div>
	</body>
</html>
  1. Erase all the stuff you don't need. If you need them anyway, go to Three-11 Github (opens new window).

# Develop

# 1. Folder/File structure

(The example is for React project with TypeScript.Тhe structure is similar with no TypeScript, just the extension on the files is not .tsx/ts but .js)
More info (opens new window)

  1. src/ directory is the directory where our project content and styles are located.
  2. assets/ is the place, where all fonts, images, icons, svgs, videos, etc. are located.
    • assets/styles/utilities here you can place all the global/generic styles, mixins, colors, etc..
  3. components/ is the directory where are all the components(stateless components). When new component is created in order to use it, we should export it, in the index.ts file, located in the components directory.
  4. containers/ is where all the container(pages(statefull components) are located. Each container can export more than one component. An example folder structure is included in (src/containers/.boilerplate).
    • enums.ts - each container has its own enums
    • interfaces.ts - each container has its own interfaces
    • reducer.ts - the container reducer
    • sagas.ts - the container sagas
  5. utilities/ all the helpers and utility functions, enums, interfaces, etc.. are located.
  6. app.scss is the Application's global SCSS entry point
  7. app.tsx is the Application's main component. The React router is used to decide which component to show and which to hide.
  8. custom.d.ts Custom type definitions
  9. index.html is the Application's HTML file 10.index.tsx - The main entry point
  10. lodables.tsx - Code split and lazy loaded components
  11. reducers.ts - Application's root reducer
  12. sagas.ts - Application's sagas
  13. settings.scss is the Application's SCSS settings (variables, mixins, etc)
  14. store.ts - Application's Redux store

# 2. Guidelines

  1. CSS/SCSS in React
    In our React projects we use SCSS syntax of Sass preprocessor.
    Sass - guidelines (opens new window)

  2. Every new component which is created should contain it's own index.tsx file, where all the JSX will be written. If this component has it's own styles, the directory should have also index.scss file. Example nav component directory should look like this:

components/
	btn/
	nav/
		index.tsx
		index.scss
  1. When importing package in a file it should follow some order. On top of the imports are all React imports, then all the other external packages and then all the local files we need. All the imports in a file should be arranged like a 'ladder' based on from word. Here is a base example.
import * as React from 'react';
import SVG from 'react-inlinesvg';
import { useSelector, useDispatch } from 'react-redux';
import * as moment from 'moment';
import { v4 as uuidv4 } from 'uuid';
import { RootStore } from '@src/store';
import { UserInterface } from '@containers/tasks/interfaces';
import { UsersActionTypes } from '@containers/tasks/enums';
import { TaskPlanning, Event } from './interfaces';
import { ExpensesActionTypes } from '@containers/budget/enums';
import { SuppliersActionTypes } from '@containers/suppliers/enums';
import { PlanningActionTypes, EventActionTypes } from '@containersplanning/enum';
import { Wrapper, CalendarComponent, Popup, Table, TableType } from '@components';
  1. Use Named Export/Imports instead of Default Export/Imports where possible, or both.

For reference:
MDN - exports (opens new window)
Default exports or named exports: Why not both? (opens new window)

// Named Imports

// ex. importing a single named export
import { MyComponent } from './MyComponent';

// ex. importing multiple named exports
import { MyComponent, MyComponent2 } from './MyComponent';

// ex. giving a named import a different name by using "as":
import { MyComponent2 as MyNewComponent } from './MyComponent';

// Named Export

// exports from ./MyComponent.js file
export const MyComponent = () => {};
export const MyComponent2 = () => {};

// Default Import

// import
import MyDefaultComponent from './MyDefaultExport';

// Default Export

// export
const MyComponent = () => {};
export default MyComponent;

// Both

//import
import { Header } from '@components';

//export
export const Header: React.FunctionComponent = () => {};
export default Header;
  1. Use Function Component(Function Expression) to create containers and components, instead of Class Component. Take these examples below. The first one is a Class Component , the second one is a Function Component. They both do exactly the same thing:
// Example stateless class component

class MyComponent extends React.Component {
	render() {
		return <p>Hello, {this.props.name}
	}
}
// Example stateless function component

function MyComponent(props) {
return <p>Hello, { props.name }</p>
}

// Example stateful class component

import React, { Component } from 'react';

class MyComponent extends Component {
	constructor(props) {
		super(props);

 			this.state = {s
			count: props.count || 0
			}

 		this.onClickHandler = this.onClickHandler.bind(this);
	}

 	public onClickHandler(e): void {
		this.setState({
			count: this.state.count + 1;
		});
	}

 	public render(): React.ReactNode {
		return (
			<div>
				<p>Count is: {this.state.count}</p>
				<button onClick={onClickHandler}>Increase Count</button>
			</div>
		);
	}
}

// Example stateful function component

import, React {useState} from 'react';

function MyComponent(props) {
const [count, setCount] = useState(props.count || 0);

    const onClickHandler = (): void => {
    	setCount(count + 1);
    };

    return (
    	<div>
    		<p>Count is: {count}</p>
    		<button onClick={onClickHandler}>Increase count</button>
    	</div>
    );

}

2.5. Use React Hooks (opens new window) instead of lifecycle methods.

// Example stateful class component
import React, { Component } from 'react';

class MyComponent extends Component {
	constructor(props) {
		super(props);

		this.state = {
			data: null,
			isLoading: false,
			error: null
		};
	}

	async loadAsyncData() {
		this.setState({ isLoading: true, error: null });

		try {
			const resp = await fetch('https://...').then(r => r.json());
			this.setState({ isLoading: false, data: resp });
		} catch (e) {
			this.setState({ isLoading: false, error: e });
		}
	}

	componentDidMount() {
		loadAsyncData();
	}

	render() {
		if (this.state.isLoading) return <p>Loading...</p>;
		if (this.state.error) return <p>Something went wrong</p>;
		if (this.state.data) return <p>The data is: {data}</p>;

		return <p>No data yet</p>;
	}
}

// Example stateful function component
import React, { useEffect, useState } from 'react';

function MyComponent() {
	const [data, setData] = useState();
	const [isLoading, setIsLoading] = useState(false);
	const [error, setError] = useState();

	const loadAsyncData = async () => {
		setIsLoading(true);
		setError(null);

		try {
			const resp = await fetch('https://...').then(r => r.json());
			setData(resp);
			setIsLoading(false);
		} catch (e) {
			setError(e);
			setIsLoading(false);
		}
	};

	useEffect(() => {
		loadAsyncData();
	}, []);

	if (this.state.isLoading) return <p>Loading...</p>;
	if (this.state.error) return <p>Something went wrong</p>;
	if (this.state.data) return <p>The data is: {data}</p>;

	return <p>No data yet</p>;
}

2.6. Use MDN - Destructuring assignment (opens new window) and rest syntax where possible.

// Bad example
const hero = {
	name: 'Batman',
	realName: 'Bruce Wayne'
};

const name = hero.name;
const realName = hero.realName;

name; // => 'Batman',
realName; // => 'Bruce Wayne'

// Good example
const hero = {
	name: 'Batman',
	realName: 'Bruce Wayne'
};

const { name, realName } = hero;

name; // => 'Batman',
realName; // => 'Bruce Wayne'

// =======

const name = hero.name;
const realName = hero.realName;

// is equivalent to:

const { name, realName } = hero;

// Rest syntax

const hero = {
	name: 'Batman',
	realName: 'Bruce Wayne'
};

const { name, ...realHero } = hero;

realHero; // => { realName: 'Bruce Wayne' }

2.7. When using TypeScript, every object, array or variable, should have their own type or interface declared. See TypeScript docs (opens new window)

const name: string = 'John';
const index: number = 3;
const isDone: boolean = true;
const lucky-numbers: number[] = [22, 18, 10];
const namesArray: ReadonlyArray<string> = ['Ivan', 'Petio', 'Bojan'];

export interface User {
	readonly id: string;
	readonly name: string;
	readonly active: boolean;
}

export interface Supplier {
	readonly id: string;
	readonly color: string;
	readonly 'supplier-name': string;
	readonly type: string;
	readonly 'contact-name': string;
	readonly 'service-description': string;
	readonly phone: string;
	readonly email: string;
	readonly status: string;
	readonly isActive: boolean;
}

# 3. Redux

We have Redux store already implemented in our React templates.

To make use of it do the following steps:

  1. Define schema for store branch
  2. Save the schema in interfaces.ts.
export interface Car {
	readonly brand: string;
	readonly model: string;
	readonly doors: number;
}

export interface CarsGroup {
	readonly title: string;
	readonly rows: Car[];
}

export interface CarActions {
	type: PlanningActionTypes;
	payload: any;
}
  1. Create enumeration for actions which are going to make changes to the store object.
export enum CarActionTypes {
	ADD_CAR = 'ADD_CAR',
	DELETE_CAR = 'DELETE_CAR',
	MODIFY_CAR = 'MODIFY_CAR'
}
  1. Create initial state
export const carsGroup: CarsGroup[] = [
	{
		title: 'Family Cars',
		rows: [
			{
				brand: 'Ford',
				model: 'Mondeo',
				doors: 5
			},

			{
				brand: 'Kia',
				model: 'Sportage',
				doors: 5
			},

			{
				brand: 'Opel',
				model: 'Insignia',
				doors: 4
			}
		]
	}
];
  1. Create reducer(reducer is pure function that take the CURRENT state of an application, perform an action, and return a NEW state)
import { CarActionTypes } from './enum';
import { CarActions } from './interfaces';
import { carsGroup } from './initial-state';

export const eventReducer = (state = carsGroup, { type, payload }: CarActions): Event[] => {
	switch (type) {
		case EventActionTypes.ADD_CAR:
			return payload;

		case EventActionTypes.DELETE_CAR:
			return payload;

		case EventActionTypes.MODIFY_CAR:
			return payload;

		default:
			return state;
	}
};
  1. Set the branch in the store
import { routerMiddleware } from 'connected-react-router';
import { composeWithDevTools } from 'redux-devtools-extension';
import { History, createBrowserHistory } from 'history';
import createSagaMiddleware, { Saga, SagaMiddleware } from 'redux-saga';
import { Store, Middleware, createStore, applyMiddleware } from 'redux';

import sagas from './sagas';
import rootReducer from './reducers';

import { AuthState, initialState as authInitialState } from '@containers/uth';
import { Cars } from '@containers/cars/interfaces'
import { carsInitialState } from '@containers/cars/initial-state'

export interface RootStore {
	auth: AuthState;
	readonly cars: carsGroup[];
}

export const history: History = createBrowserHistory();
export const sagaMiddleware: SagaMiddleware = createSagaMiddleware();

export function configureStore(): Store<RootStore> {
	const historyMiddleware: Middleware = routerMiddleware(history);

	const store: Store<RootStore> = createStore(
		rootReducer(history),
		{
			auth: authInitialState;
			cars: carsInitialState;

		},
		composeWithDevTools(applyMiddleware(sagaMiddleware, historyMiddleware))
 );

	if (module.hot) {
		module.hot.accept();

      // eslint-disable-next-line
		store.replaceReducer(require('./reducers').default(history));
	}

	sagas.forEach((saga: Saga) => {
		sagaMiddleware.run(saga);
	});

	return store;
}
  1. Select the branch in the component and use it instead of local state
const carsState = useSelector((state: RootStore) => state.cars);
  1. Dispatch actions (defined in enums.ts) to update the branch
const dispatch = useDispatch();
const handleAddCarClick = (): void => {
	dispatch({
		type: EventActionTypes.ADD_CAR,
		payload: [
			{
				brand: 'Reno',
				model: 'Talisman',
				doors: 4
			}
		]
	});
};

After all the steps your component's folder should look something like this:

 component/
 	enums.ts
 	index.tsx
 	initial-state.ts
 	interfaces.ts
 	reducer.ts

# 4. Redux - Saga

Redux-Saga is a library that aims to make application side effects (i.e. asynchronous things like data fetching and impure things like accessing the browser cache) easier to manage, more efficient to execute, easy to test, and better at handling failures.

The mental model is that a saga is like a separate thread in your application that's solely responsible for side effects. Redux-Saga is a redux middleware, which means this thread can be started, paused and cancelled from the main application with normal redux actions, it has access to the full redux application state and it can dispatch redux actions as well.

Redux - Saga docs (opens new window)

We already have Redux-Saga implemented in our React templates. To start using it, you should do the following steps.

  1. Go to sagas.js and define your saga. We'll create a Saga that watches for all USER_FETCH_DATA actions and triggers an API call to fetch the user data.
import { call, put, takeEvery, takeLatest } from 'redux-saga/effects';
import Api from '...';

// worker Saga: will be fired on USER_FETCH_DATA actions
export function* fetchUserData(action) {
	try {
		const user = yield call(Api.fetchUserData, action.payload.userId);

		// handle success

		yield put({ type: 'USER_FETCH_SUCCEEDED', user: user });
	} catch (e) {
		// handle error

		yield put({ type: 'USER_FETCH_FAILED', message: e.message });
	}
}

// Starts fetchUserData on each dispatched `USER_FETCH_DATA` action.

export function* getUserData() {
	yield takeLatest('USER_FETCH_DATA', fetchUserData);
}
  1. Dispatch action from the component (on some button click) to fetch user data. The Component dispatches a plain Object action to the Store
 export const Component {
  ...
   onButtonClicked() {
     dispatch({type: 'USER_FETCH_DATA'})
   }
  ...
 }