Skip to content

Latest commit

 

History

History
443 lines (338 loc) · 10.7 KB

01-react-first-project.md

File metadata and controls

443 lines (338 loc) · 10.7 KB

React - First Project

Starting the Project

npx create-react-app project-name --template=typescript
cd project-name
rm README.md
cd src
rm App.css App.test.tsx index.css logo.svg serviceWorker.ts
# remove the references to the deleted files in index.tsx and App.tsx
# write an React.FC with a 'Hello World' in the App.tsx
cd ../public
rm favicon.ico logo192.png logo512.png manifest.json
# remove the references to the deleted files in index.html

public/index.html:

<meta name="theme-color" content="#3a3a3a" />
<title>GitHub Explorer</title>

Editor Config

root = true

[*]
indent_style = space
indent_size = 2
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
end_of_line = lf

ESLint

Assuming the project was created with create-react-app.

In the package.json REMOVE this part:

"eslintConfig": {
  "extends": "react-app"
},

Install eslint and initialize it:

yarn add eslint
yarn eslint --init
# 1. To check syntax, find problems and enforce code style
# 2. JavaScript modules (impot/export)
# 3. React
# 4. (use TypeScript?) Yes
# 5. (mark only Browser with space bar and then Enter)
# 6. Use a popular style guide
# 7. Airbnb
# 8. JSON
# 9. (install with npm?) No

# copy the packages shown on the question 9 except 'eslint@^5.16.0 || ^6.8.0'
# because we already have it, and remove 1.7.0 from
# 'eslint-plugin-react-hooks@^2.5.0 || ^1.7.0'. The command will become like this:
yarn add eslint-plugin-react@^7.19.0 @typescript-eslint/eslint-plugin@latest eslint-config-airbnb@latest eslint-plugin-import@^2.20.1 eslint-plugin-jsx-a11y@^6.2.3 eslint-plugin-react-hooks@^2.5.0 @typescript-eslint/parser@latest -D

# making ReactJS undertand TypeScrpt
yarn add eslint-import-resolver-typescript -D

Create the .eslingignore file:

**/*.js
node_modules
build
/src/react-app-env.d.ts

Edit .eslintrc.json:

{
  // ...
  "extends": [
    "plugin:react/recommended"
    // ...
    "plugin:@typescript-eslint/recommended"
  ],
  // ...
  "plugins": [
    // ...
    "react-hooks",
    "@typescript-eslint"
  ],
  "rules": {
    "react-hooks/rules-of-hooks": "error",
    "react-hooks/exhaustive-deps": "warn",
    "import/prefer-default-export": "off",
    "import/extensions": [
      "error",
      "ignorePackages",
      {
        "ts": "never",
        "tsx": "never"
      }
    ]
  },
  // ...
  "settings": {
    "import/resolver": {
      "typescript": {}
    }
  }
  // ...
}

Note: if you're facing a warning like 'React' was used before it was defined, add this to the .eslintrc.json:

"rules": {
  // ...
  "no-use-before-define": "off",
  // ...
},

Prettier

It's the same for NodeJS, ReactJS and React Native.

yarn add prettier eslint-config-prettier eslint-plugin-prettier -D

Edit the .eslintrc.json:

{
  // ...
  "extends": [
    "prettier/@typescript-eslint",
    "plugin:prettier/recommended",
  ],
  // ...
  "plugins": [
    // ...
    "prettier"
  ],
  "rules": {
    // ...
    "prettier/prettier": "error"
  },
  // ...
}

Solving conflicts between ESLint and Prettier.

prettier.config.js

module.exports = {
  singleQuote: true,
  trailingComma: 'es5',
  arrowParens: 'avoid',
}

.eslintignore:

# ...
/*.js

Restart VS Code.

Creating Routes

yarn add react-router-dom
yarn add -D @types/react-router-dom
  1. App.tsx:
<BrowserRouter>
  <Routes>
</BrowserRouter>
  1. routes/index.tsx:
<Switch>
  <Route path="/" exact component={Dashboard} />
  <Route path="/repositories" component={Repository} />
</Switch>
  1. Create the pages/*.tsx

Short way to declare the type a React's function component: React.FC.

import React from 'react';

const Dashboard: React.FC = () => <h1>Dashboard</h1>;

export default Dashboard;

Why using <Switch> as a parent of <Route>s? To render only the first child <Route> that matches the location.

Note: <Switch> must be used inside a Router (such as <BrowserRouter>).

Use exact to match the exact route. Example:

<Route path="/" exact component={Dashboard} />

Using Styled Components

Why styled-components?

Styled components is a way to create a React component with the styles attached to it.

From the styled-component docs:

It removes the mapping between components and styles. This means that when you're defining your styles, you're actually creating a normal React component, that has your styles attached to it.

Don't forget: a styled component is a React component (therefore you can pass props to it).

yarn add styled-components
yarn add -D styled-components

Install the VS Code plugin: vscode-styled-components. It helps with the intellisense.

Styled components allows you to use css inside the JavaScript:

import styled from 'styled-components';

export const Title = styled.h1`
  font-size: 48px;
  color: #3a3a3a;
`;

For global styling use createGlobaStyle, link in this src/styles/globals.ts:

import { createGlobalStyle } from 'styled-components';

export default createGlobalStyle`
  * {
    margin: 0;
    padding: 0;
    outline: 0;
    box-sizing: border-box;
  }

  body {
    background: #f0f0f5;
  }
`;

Styling the Dashboard

Styled components accept CSS preprocessing. Example:

export const Form = styled.form`
  button {
    background: #333
    /* ... */

    &:hover {
      background: #111;
    }
  }

& meaning the current element (in the example above it's the button).

One more lib for writing styles in JavaScript:

yarn add polished
yarn add react-icons

In this example we're going to use the shade() method:

background: ${shade(0.2, '#444')};

Use transition so the background can change smoothly:

transition: background-color 0.2s;

Connecting the API

yarn add axios

src/services/api.ts:

import axios from 'axios';

const api = axios.create({
  baseURL: 'https://api.github.com'
});

export default api;

Handling Errors

Accessing props inside the styles. Example:

In the Form's client:

<Form hasError={!!inputError}>
  //...
</Form>

In the Form's code:

import styled, { css } from 'styled-components';

interface FormProps {
  hasError: boolean;
}

export const Form = styled.form<FormProps>`
  input {
    ${(props) => props.hasError && css`
      border-color: #c53030;
    `}
  }
`

Saving Data in the localStorage

const [repositories, setRepositories] = useState<Repository[]>(() => {
  const storedRepositories = localStorage.getItem(
    '@GithubExplorer:repositories',
  );

  if (storedRepositories) {
    return JSON.parse(storedRepositories);
  } else {
    return [];
  }
});

useEffect(() => {
  localStorage.setItem(
    '@GithubExplorer:repositories',
    JSON.stringify(repositories)
  );
}, [repositories]);

Browsing Between Routes

Using Link from react-router-dom. It's just replacing a with Link and href with to.

In order to get everything after the route as a route param, use the + sign, like this: path="/repositories/:repository+.

Disable react/jsx-one-expression-per-line when there are text a variables in the same line (it confuses the eslint).

Styling Details

Tip: avoid to style more than two children of an element/component.

Listing Issues

In the video it's shown how useful it is to have the eslint plugin react-hooks.

.eslintrc.json:

"plugins": [
  "react-hooks"
],
"rules": {
  "rect-hooks/exhaustive-deps": "warn"
}

There's an useful tip about how beneficial it is to use .then() instead of async-await. Multiple calls with .then() allows the functions to run in parallel (asynchronously), improving the perceived speed.

Optional chaining: instead of repository && repository.owner, use repository?.owner.