From 2824575d2936df4ef3c7d1a99f14caa0b278a7ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Beumier?= Date: Thu, 31 Oct 2024 16:20:05 +0100 Subject: [PATCH] rearrange file --- React.js | 5029 +++++++++++++++++++++++++----------------------------- 1 file changed, 2328 insertions(+), 2701 deletions(-) diff --git a/React.js b/React.js index 0ccbcf6..53187e3 100644 --- a/React.js +++ b/React.js @@ -18,31 +18,6 @@ -// SETUP DEV - -// start a new react project from scratch -> https://jscomplete.com/learn/1rd-reactful -cd projectFolder -// create package.json -npm init -y -// express to run node server -npm i express -// install react & react-dom -npm i react react-dom -// install webpack, a module bundler -npm i webpack webpack-cli -// install babel -npm i babel-loader @babel/core @babel/node @babel/preset-env @babel/preset-react -// dev dependencies -// nodemon or alternative to change server code without restarting node -npm i -D nodemon -// eslint -> add a .eslintrc.json file -npm i -D eslint babel-eslint eslint-plugin-react eslint-plugin-react-hooks -/* eslint-config-prettier eslint-config-airbnb eslint-plugin-cypress eslint-plugin-import eslint-plugin-jest eslint-plugin-jsx-a11y eslint-plugin-prettier */ -// jest for testing -npm i -D jest babel-jest react-test-renderer - - - // start a new react project with a builder create-react-app project-name create-react-app project-name --template typescript // to have typescript set up @@ -50,3091 +25,2743 @@ npm start -// very useful to install react devtool +// FUNCTIONAL COMPONENTS +// state, ref and lyfecycle arrived in react v16.8 with hooks +// still can't use componentDidError and getSnapshotBeforeUpdate +// React Components +const FunctionalComponent() { + return

Hello world

; +}; +const FunctionalComponent = () => ( +

Hello world

; +); -// package.json -{ - "name": "ps-redux", - "description": "React and Redux Pluralsight course by Cory House", - "scripts": { - "start": "webpack serve --config webpack.config.dev.js --port 3000" - }, - "dependencies": { - "bootstrap": "5.0.2", - "immer": "9.0.5", - "prop-types": "15.7.2", - "react": "17.0.2", - "react-dom": "17.0.2", - "react-redux": "7.2.4", - "react-router-dom": "5.2.0", - "react-toastify": "7.0.4", - "redux": "4.1.0", - "redux-thunk": "2.3.0", - "reselect": "4.0.0" - }, - "devDependencies": { - "@babel/core": "7.14.6", - "@testing-library/react": "^12.0.0", - "@wojtekmaj/enzyme-adapter-react-17": "^0.6.2", - "babel-eslint": "10.1.0", - "babel-loader": "8.2.2", - "babel-preset-react-app": "10.0.0", - "css-loader": "5.2.6", - "cssnano": "5.0.6", - "enzyme": "3.11.0", - "eslint": "7.30.0", - "eslint-loader": "4.0.2", - "eslint-plugin-import": "2.23.4", - "eslint-plugin-react": "7.24.0", - "fetch-mock": "9.11.0", - "html-webpack-plugin": "5.3.2", - "http-server": "0.12.3", - "jest": "27.0.6", - "json-server": "0.16.3", - "mini-css-extract-plugin": "2.1.0", - "node-fetch": "^2.6.1", - "npm-run-all": "4.1.5", - "postcss": "^8.3.5", - "postcss-loader": "6.1.1", - "react-test-renderer": "17.0.2", - "redux-immutable-state-invariant": "2.1.0", - "redux-mock-store": "1.5.4", - "rimraf": "3.0.2", - "style-loader": "3.0.0", - "webpack": "5.44.0", - "webpack-bundle-analyzer": "4.4.2", - "webpack-cli": "4.9.0", - "webpack-dev-server": "3.11.2" - }, - "engines": { - "node": ">=8" - }, - "babel": { - "presets": [ - "babel-preset-react-app" - ] - }, - "eslintConfig": { - "extends": [ - "eslint:recommended", - "plugin:react/recommended", - "plugin:import/errors", - "plugin:import/warnings" - ], - "parser": "babel-eslint", - "parserOptions": { - "ecmaVersion": 2018, - "sourceType": "module", - "ecmaFeatures": { - "jsx": true - } - }, - "env": { - "browser": true, - "node": true, - "es6": true, - "jest": true - }, - "rules": { - "no-debugger": "off", - "no-console": "off", - "no-unused-vars": "warn", - "react/prop-types": "warn" - }, - "settings": { - "react": { - "version": "detect" - } - }, - "root": true - } + + +// React Component multiline +const FunctionalComponent() { + return ( +
+

Hello world

+

How you doing?

+
+ ); } +const FunctionalComponent = () => ( +
+

Hello world

+

How you doing?

+
+); +// Render Component +import React from "react"; +const FunctionalComponent = () => { return

Hello world

; }; +ReactDOM.render(, document.getElementById('app')); +// OR +import React from "react"; +const FunctionalComponent = () => { return

Hello world

; }; +export default FunctionalComponent; +// OR +import React from "react"; +export default function FunctionalComponent() { return

Hello world

; }; + -// webpack.config.dev.js -const webpack = require('webpack'); -const path = require('path'); -const HtmlWebpackPlugin = require('html-webpack-plugin'); -process.env.NODE_ENV = 'development'; -module.exports = { - mode: 'development', - target: 'web', - devtool: 'cheap-module-source-map', - entry: './src/index', - output: { - path: path.resolve(__dirname, 'build'), - publicPath: '/', - filename: 'bundle.js', - }, - devServer: { - stats: 'minimal', - overlay: true, - historyApiFallback: true, - disableHostCheck: true, - headers: { 'Access-Control-Allow-Oriigin': '*' }, - https: false, - }, - plugins: [ - new HtmlWebpackPlugin({ - template: 'src/index.html', - favicon: 'src/favicon.ico', - }), - ], - module: { - rules: [ - { - test: /\.(js|jsx)$/, - exclude: /node_modules/, - use: ['babel-loader', 'eslint-loader'], - }, - { - test: /(\.css)$/, - use: ['style-loader', 'css-loader'], - }, - ], - }, -}; +// Pay attention to the export method !!! +export default function ComponentName() {}; +import ComponentName from './path'; +const ComponentName = () => {}; +export default ComponentName; +import ComponentName from './path'; +export function ComponentName() {}; +import { ComponentName } from './path'; -// SET UP PRODUCTION BUILD +export const ComponentName = () => {}; +import { ComponentName } from './path'; -// the goal is to end up with a build folder composed of index.html, bundle.js and styles.css -// package.json -{ - "name": "ps-redux", - "description": "React and Redux Pluralsight course by Cory House", - "scripts": { - "start": "run-p start:dev start:api", - "start:dev": "webpack serve --config webpack.config.dev.js --port 3000", - "prestart:api": "node tools/createMockDb.js", - "start:api": "node tools/apiServer.js", - "test": "jest --watchAll", - "test:ci": "jest", - "clean:build": "rimraf ./build && mkdir build", - "prebuild": "run-p clean:build test:ci", - "build": "webpack --config webpack.config.prod.js", - "postbuild": "run-p start:api serve:build", - "serve:build": "http-server ./build" - }, - "jest": { - "setupFiles": [ - "./tools/testSetup.js" - ], - "testEnvironment": "jsdom", - "moduleNameMapper": { - "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "/tools/fileMock.js", - "\\.(css|less)$": "/tools/styleMock.js" - } - }, - "dependencies": { - "bootstrap": "5.0.2", - "immer": "9.0.5", - "prop-types": "15.7.2", - "react": "17.0.2", - "react-dom": "17.0.2", - "react-redux": "7.2.4", - "react-router-dom": "5.2.0", - "react-toastify": "7.0.4", - "redux": "4.1.0", - "redux-thunk": "2.3.0", - "reselect": "4.0.0" - }, - "devDependencies": { - "@babel/core": "7.14.6", - "@testing-library/react": "^12.0.0", - "@wojtekmaj/enzyme-adapter-react-17": "^0.6.2", - "babel-eslint": "10.1.0", - "babel-loader": "8.2.2", - "babel-preset-react-app": "10.0.0", - "css-loader": "5.2.6", - "cssnano": "5.0.6", - "enzyme": "3.11.0", - "eslint": "7.30.0", - "eslint-loader": "4.0.2", - "eslint-plugin-import": "2.23.4", - "eslint-plugin-react": "7.24.0", - "fetch-mock": "9.11.0", - "html-webpack-plugin": "5.3.2", - "http-server": "0.12.3", - "jest": "27.0.6", - "json-server": "0.16.3", - "mini-css-extract-plugin": "2.1.0", - "node-fetch": "^2.6.1", - "npm-run-all": "4.1.5", - "postcss": "^8.3.5", - "postcss-loader": "6.1.1", - "react-test-renderer": "17.0.2", - "redux-immutable-state-invariant": "2.1.0", - "redux-mock-store": "1.5.4", - "rimraf": "3.0.2", - "style-loader": "3.0.0", - "webpack": "5.44.0", - "webpack-bundle-analyzer": "4.4.2", - "webpack-cli": "4.9.0", - "webpack-dev-server": "3.11.2" - }, - "engines": { - "node": ">=8" - }, - "babel": { - "presets": [ - "babel-preset-react-app" - ] - }, - "eslintConfig": { - "extends": [ - "eslint:recommended", - "plugin:react/recommended", - "plugin:import/errors", - "plugin:import/warnings" - ], - "parser": "babel-eslint", - "parserOptions": { - "ecmaVersion": 2018, - "sourceType": "module", - "ecmaFeatures": { - "jsx": true - } - }, - "env": { - "browser": true, - "node": true, - "es6": true, - "jest": true - }, - "rules": { - "no-debugger": "off", - "no-console": "off", - "no-unused-vars": "warn", - "react/prop-types": "warn" - }, - "settings": { - "react": { - "version": "detect" - } - }, - "root": true - } -} - -// webpack.config.prod.js -const webpack = require('webpack'); -const path = require('path'); -const HtmlWebpackPlugin = require('html-webpack-plugin'); -const MiniCssExtractPlugin = require('mini-css-extract-plugin'); -const webpackBundleAnalyzer = require('webpack-bundle-analyzer'); -process.env.NODE_ENV = 'production'; -module.exports = { - mode: 'production', - target: 'web', - devtool: 'source-map', - entry: './src/index', - output: { - path: path.resolve(__dirname, 'build'), - publicPath: '/', - filename: 'bundle.js', - }, - plugins: [ - new webpackBundleAnalyzer.BundleAnalyzerPlugin({ analyzerMode: 'static' }), - new MiniCssExtractPlugin({ - filename: '[name].[contenthash].css', - }), - new webpack.DefinePlugin({ - 'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV), - 'process.env.API_URL': JSON.stringify('http://localhost:3001'), - }), - new HtmlWebpackPlugin({ - template: 'src/index.html', - favicon: 'src/favicon.ico', - minify: { - // see https://github.com/kangax/html-minifier#options-quick-reference - removeComments: true, - collapseWhitespace: true, - removeRedundantAttributes: true, - useShortDoctype: true, - removeEmptyAttributes: true, - removeStyleLinkTypeAttributes: true, - keepClosingSlash: true, - minifyJS: true, - minifyCSS: true, - minifyURLs: true, - }, - }), - ], - module: { - rules: [ - { - test: /\.(js|jsx)$/, - exclude: /node_modules/, - use: ['babel-loader', 'eslint-loader'], - }, - { - test: /(\.css)$/, - use: [ - MiniCssExtractPlugin.loader, - { - loader: 'css-loader', - options: { - sourceMap: true, - }, - }, - { - loader: 'postcss-loader', - options: { - postcssOptions: { - plugins: [() => [require('cssnano')]], - }, - sourceMap: true, - }, - }, - ], - }, - ], - }, +// PROPS +import PropTypes from 'prop-types'; // needs 'npm install prop-types' +const FunctionalComponent = (props) => { + return

Hello, {props.name}

; +}; +// OR +const FunctionalComponent = ({ name }) => { + return

Hello, {name}

; +}; +// OR +const FunctionalComponent = ({ name, ...props }) => { + return

Hello, {name} {props.surname}

; }; +// prop types arrayOf(number), objectOf(string)) -> https://reactjs.org/docs/typechecking-with-proptypes.html +FunctionalComponent.propTypes = { + name: PropTypes.string.isRequired, + age: PropTypes.number, + fun: PropTypes.bool, + arr: PropTypes.array, + obj: PropTypes.obj, + el: PropTypes.element, + one: PropTypes.oneOf([number, string]) +}; +// default props +FunctionalComponent.defaultProps = { age: 16 }; +} one={1} /> +// PROPS FROM PARENT TO CHILD +import { ChildComponent } from './ChildComponent'; +const ParentComponent = () => { + return ; +}; +export const ChildComponent = ({ name, ...props }) => { + return

Hello, {name}

+}; -// BASICS +// PROPS FROM CHILD TO PARENT +import { ChildComponent } from './ChildComponent'; +const ParentComponent = () => { + const getFromChild = (data) => { + console.log(data); + } + return ; +}; +export const ChildComponent = ({ func, ...props }) => { + func('This is data') + return <> +}; -// use those import everytime! They are needed in all examples -import React from "react"; -import ReactDOM from "react-dom"; +// STATE HOOK +import { useState } from 'react'; +const FunctionalComponent = () => { + const [count, setCount] = useState(0); + return ( +
+

count: {count}

+ + {/* better way to use current state */} + +
+ ); +}; +// OR conditional rendering based on state +const FunctionalComponent = () => { + const [show, setShow] = useState(false); + return ( +
+ + {show &&

{show ? `Ì'm visible` : `Ì'm not visible`}

} +
+ ); +}; -// write JSX elements just as HTML -const h1 =

React JS

; -// define element attribute just as in HTML -const a = What a link!; +// EFFECT HOOK +import { useEffect } from 'react'; +const FunctionalComponent = () => { + // On Mounting + useEffect( () => console.log("mount"), [] ); + // update specific data + useEffect( () => console.log("will update data"), [ data ] ); + // update all + useEffect( () => console.log("will update any") ); + // update specific data on unmount + useEffect( () => () => console.log("will update data on unmount"), [ data ] ); + // On Unmounting + useEffect( () => () => console.log("unmount"), [] ); + // updated data returned + return

{data}

; +}; -// multiline element are possible using parenthesis () -const ah1 = ( - -

- What a big link! -

-
-); +// skip first render with useEffect +import { useEffect, useRef } from 'react'; +const FunctionalComponent = () => { + // useref to avoid wasted renders + const notInitialRender = useRef(false) + useEffect(() => { + if (notInitialRender.current) { + // do your magic here + } else { + notInitialRender.current = true + } + }, [data]) + return

{data}

; +} -// a JSX expression must have exactly one outermost element -// good habit is to have a
, or <> (, ) wrapping everything -const blog = ( -
-

Main title

-

Subtitle

-
-); +// REF HOOK +import { useRef } from 'react'; +const FunctionalComponent = () => { + const inputEl = useRef(null); + const onButtonClick = () => { + // `current` points to the mounted text input element + inputEl.current.focus(); + }; + return ( + <> + + + + ); +} -// render your HTML; first argument is the JSX element and the second points to the HTML where it will be rendered -// ->
-ReactDOM.render(

Hello world

, document.getElementById("app")); -// you can use variable of course -const myElt =

Render me!

; -ReactDOM.render(myElt, document.getElementById("app")); +// REDUCER HOOK +// useReducer instead of useState for complex state logic +import { useReducer } from 'react'; +function reducer(state, action) { + switch (action.type) { + case 'increment': + return {count: state.count + 1}; + case 'decrement': + return {count: state.count - 1}; + default: + throw new Error(); + } +} +const FunctionalComponent = () => { + const [state, dispatch] = useReducer(reducer, {count: 0}); + return ( +
+

count: {state.count}

+ + +
+ ); +}; -// or create it without JSX -const myElt = React.createElement("h1", null, "Hello world"); -ReactDOM.render(myElt, document.getElementById("app")); +// MEMO HOOK +// memoize a function to update only when a dependency prop has changed in the array +import { useMemo } from 'react'; +const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]); -// class property is special, contrary to HTML, JSX need it to be called className -const myDiv =
; -// self closing tags as well, they NEED the back slash -const myImg = ; -const myBr =
; - -// javascript into JSX thanks to curly braces {} -const text = "The result of 2 + 3 is:"; -const myJS =

{text + " " + 2 + 3}

; -// you can have comments that way -const myCom = ( -
-

Comment

- {/* here is a commented text */} -
+// CALLBACK HOOK +// memoize a returned value to update only when a dependency prop has changed in the array +import { useCallback } from 'react'; +const memoizedCallback = useCallback( + () => { + doSomething(a, b); + }, + [a, b], ); -// create event listeners -function myFunc() { - alert("Click on this image"); + +// CONTEXT HOOK +// very useful to pass data deep in the tree +import { createContext, useState } from 'react'; +export const CountContext = createContext(); +const FunctionalComponent = () => { + const [count, setCount] = useState(0); + return ( + + + + ) } - +import { useContext } from 'react'; +import { CountContext } from './FunctionalComponent'; +const ChildComponent = () => { + const {setCount, count} = useContext(CountContext); + return ( +
+

count: {count}

+ +
+ ) +} -// if else are not possibile inside JSX --> use ternary operator -const isTrue =

{1 === 1 ? "true" : "false"}

; -// JSX conditionals; will render the HTML or not based on the left of the logical operator -const showParagraph = true; -const myDiv = ( -
{showParagraph &&

I'm rendered because the const is true

}
-); -const hideParagraph = false; -const myDiv = ( -
{hideParagraph ||

I'm rendered because the const is false

}
-); -// map method and JSX; React understand it needs to make a list out of the array -// use key attribute to make list item identifiable! makte each key unique and avoid index!! -const numbers = ["one", "two", "three"]; -const list = numbers.map((number, i) =>
  • {number}
  • ); -ReactDOM.render(
      {list}
    , document.getElementById("app")); -// filter helps with filtering maps -const numbers = [ - {n: "one", ok: true}, - {n: "twoo", ok: false}, - {n: "three", ok: true} -]; -const list = numbers.filter(number => number.ok).map((numberFiltered, i) =>
  • {numberFiltered.n}
  • ); -ReactDOM.render(
      {list}
    , document.getElementById("app")); -// COMPONENTS +// PORTAL +// creates node out of parent; body or anywhere else +import { useState } from 'react'; +import { createPortal } from 'react-dom'; +export default function Modal() { + const [showModal, setShowModal] = useState(false); + return ( + <> + + {showModal && + createPortal( +
    +

    Modal title

    + +
    , + document.body + )} + + ); +} -// React Component -class MyComponentClass extends React.Component { - render() { - return

    Hello world

    ; - } -}; -ReactDOM.render(, document.getElementById('app')); -// React Component multiline -class QuoteMaker extends React.Component { - render() { - return ( -
    -

    The world is full of objects, more or less interesting; I do not wish to add any more.

    - Douglas Huebler -
    - ); - } -}; -ReactDOM.render(, document.getElementById('app')); + +// EVENTS +const EventComponents = () => ( + <> + + + + + + + + + + + + + + + + {/* and many more -> https://reactjs.org/docs/events.html */} + +) -// React Component with variables -const owl = { - title: 'Excellent Owl', - src: 'https://s3.amazonaws.com/codecademy-content/courses/React/react_photo-owl.jpg' -}; -class Owl extends React.Component { - render() { - return ( -
    -

    {owl.title}

    - {owl.title} -
    - ); - } -} -ReactDOM.render(, document.getElementById('app')); -// React Component render with logic -class Random extends React.Component { - render() { - const n = Math.floor(Math.random() * 10 + 1); - return

    The number is {n}!

    ; +// FORMS (controlled, react manage the state of the form) +import { useState } from 'react'; +const ControlledInput = () => { + const [input, setInput] = useState(''); + const [textarea, setTextarea] = useState(''); + const [select, setSelect] = useState(1); + const [checkbox, setCheckbox] = useState(false); + const [radio, setRadio] = useState(false); + const [radioString, setRadioString] = useState(''); + function handleChange(e) { + setInput(() => e.target.value); } -} -ReactDOM.render(, document.getElementById('app')); + return ( +
    +
    + + + {/* external OR inline onChange function */} + setInput(e.target.value)} /> +