├── .github ├── FUNDING.yml └── workflows │ └── deploy.yml ├── .gitignore ├── .husky └── pre-push ├── .posthtmlrc ├── .prettierrc ├── .vscode └── reveal.code-snippets ├── CHANGELOG.md ├── DONATIONS.MD ├── LICENSE ├── PROGRAM.md ├── README.md ├── SUMMARY.md ├── course.json ├── devlog.md ├── keywords.txt ├── package-lock.json ├── package.json ├── presentations ├── about │ ├── body.html │ └── index.html ├── pet-project │ ├── body.html │ ├── img │ │ ├── angularwebtemplate.png │ │ └── projects.png │ └── index.html ├── react-change-detection │ ├── body.html │ └── index.html ├── react-components │ ├── body.html │ └── index.html ├── react-cra │ ├── body.html │ ├── img │ │ ├── app-tsx.png │ │ ├── create-react-app-project.png │ │ ├── create-react-app-ui-example.png │ │ ├── index-html.png │ │ ├── index-tsx.png │ │ ├── package-json.png │ │ └── test-tsx.png │ └── index.html ├── react-forms │ ├── body.html │ ├── code │ │ ├── default-form.tsx │ │ └── yup.tsx │ └── index.html ├── react-hooks │ ├── body.html │ ├── code │ │ └── useReducer.tsx │ └── index.html ├── react-intro │ ├── body.html │ ├── img │ │ ├── old.png │ │ ├── pb24.png │ │ └── react-ang-vue-downloads-020422.png │ └── index.html ├── react-network │ ├── body.html │ ├── code │ │ ├── fetch-axios.tsx │ │ ├── fetch-cb.tsx │ │ ├── fetch-functional.tsx │ │ └── fetch.tsx │ └── index.html ├── react-new-component │ ├── body.html │ ├── code │ │ ├── counter-cb.tsx │ │ ├── counter-functional.tsx │ │ ├── mb-class.tsx │ │ └── mb-functional.tsx │ └── index.html ├── react-router │ ├── body.html │ ├── code │ │ └── react-routing.tsx │ └── index.html ├── react-state-management │ ├── body.html │ ├── code │ │ ├── index_ctx.tsx │ │ ├── index_general.tsx │ │ ├── index_mobx.tsx │ │ └── index_rdx.tsx │ └── index.html ├── react-styling │ ├── body.html │ └── index.html ├── react-testing │ ├── body.html │ ├── imgs │ │ ├── failed-example.png │ │ ├── pyramide.png │ │ └── snapshottesting.png │ └── index.html ├── react-ui-lib │ ├── body.html │ └── index.html ├── shared │ ├── helpers.css │ ├── imgs │ │ ├── 39900370_1138320566319759_9157901823137284096_n.jpg │ │ ├── Itera-logo-white-fuchsia.jpg │ │ ├── babich.jpg │ │ ├── njsr.png │ │ └── title.png │ ├── index.css │ ├── init.js │ └── vscode.css ├── solid │ ├── body.html │ ├── dip.png │ ├── index.html │ ├── isp.jpg │ ├── iuser-big.png │ ├── lsp-false.png │ ├── ocp.jpg │ ├── owl.png │ └── srp.jpg ├── typescript-intro │ ├── body.html │ └── index.html └── web-tools │ ├── body.html │ └── index.html ├── templates ├── _body.html ├── _full-program.md ├── _layout.html └── _readme.md └── tools ├── docs.js ├── encode.js ├── fs.js ├── game.mjs ├── game.tools.mjs ├── mask.js ├── scaffold.js └── start.js /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | open_collective: farstar 4 | -------------------------------------------------------------------------------- /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | name: Build and Deploy 2 | on: 3 | push: 4 | branches: 5 | - master 6 | jobs: 7 | build-and-deploy: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - name: Checkout 11 | uses: actions/checkout@v3 12 | 13 | - name: Install and Build 14 | run: | 15 | npm ci 16 | npm run build 17 | 18 | - name: Deploy 19 | uses: JamesIves/github-pages-deploy-action@v4.2.5 20 | with: 21 | branch: gh-pages # The branch the action should deploy to. 22 | folder: dist # The folder the action should deploy. -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Aa][Rr][Mm]/ 27 | [Aa][Rr][Mm]64/ 28 | bld/ 29 | [Bb]in/ 30 | [Oo]bj/ 31 | [Ll]og/ 32 | [Ll]ogs/ 33 | 34 | # Visual Studio 2015/2017 cache/options directory 35 | .vs/ 36 | # Uncomment if you have tasks that create the project's static files in wwwroot 37 | #wwwroot/ 38 | 39 | # Visual Studio 2017 auto generated files 40 | Generated\ Files/ 41 | 42 | # MSTest test Results 43 | [Tt]est[Rr]esult*/ 44 | [Bb]uild[Ll]og.* 45 | 46 | # NUnit 47 | *.VisualState.xml 48 | TestResult.xml 49 | nunit-*.xml 50 | 51 | # Build Results of an ATL Project 52 | [Dd]ebugPS/ 53 | [Rr]eleasePS/ 54 | dlldata.c 55 | 56 | # Benchmark Results 57 | BenchmarkDotNet.Artifacts/ 58 | 59 | # .NET Core 60 | project.lock.json 61 | project.fragment.lock.json 62 | artifacts/ 63 | 64 | # StyleCop 65 | StyleCopReport.xml 66 | 67 | # Files built by Visual Studio 68 | *_i.c 69 | *_p.c 70 | *_h.h 71 | *.ilk 72 | *.meta 73 | *.obj 74 | *.iobj 75 | *.pch 76 | *.pdb 77 | *.ipdb 78 | *.pgc 79 | *.pgd 80 | *.rsp 81 | *.sbr 82 | *.tlb 83 | *.tli 84 | *.tlh 85 | *.tmp 86 | *.tmp_proj 87 | *_wpftmp.csproj 88 | *.log 89 | *.vspscc 90 | *.vssscc 91 | .builds 92 | *.pidb 93 | *.svclog 94 | *.scc 95 | 96 | # Chutzpah Test files 97 | _Chutzpah* 98 | 99 | # Visual C++ cache files 100 | ipch/ 101 | *.aps 102 | *.ncb 103 | *.opendb 104 | *.opensdf 105 | *.sdf 106 | *.cachefile 107 | *.VC.db 108 | *.VC.VC.opendb 109 | 110 | # Visual Studio profiler 111 | *.psess 112 | *.vsp 113 | *.vspx 114 | *.sap 115 | 116 | # Visual Studio Trace Files 117 | *.e2e 118 | 119 | # TFS 2012 Local Workspace 120 | $tf/ 121 | 122 | # Guidance Automation Toolkit 123 | *.gpState 124 | 125 | # ReSharper is a .NET coding add-in 126 | _ReSharper*/ 127 | *.[Rr]e[Ss]harper 128 | *.DotSettings.user 129 | 130 | # TeamCity is a build add-in 131 | _TeamCity* 132 | 133 | # DotCover is a Code Coverage Tool 134 | *.dotCover 135 | 136 | # AxoCover is a Code Coverage Tool 137 | .axoCover/* 138 | !.axoCover/settings.json 139 | 140 | # Visual Studio code coverage results 141 | *.coverage 142 | *.coveragexml 143 | 144 | # NCrunch 145 | _NCrunch_* 146 | .*crunch*.local.xml 147 | nCrunchTemp_* 148 | 149 | # MightyMoose 150 | *.mm.* 151 | AutoTest.Net/ 152 | 153 | # Web workbench (sass) 154 | .sass-cache/ 155 | 156 | # Installshield output folder 157 | [Ee]xpress/ 158 | 159 | # DocProject is a documentation generator add-in 160 | DocProject/buildhelp/ 161 | DocProject/Help/*.HxT 162 | DocProject/Help/*.HxC 163 | DocProject/Help/*.hhc 164 | DocProject/Help/*.hhk 165 | DocProject/Help/*.hhp 166 | DocProject/Help/Html2 167 | DocProject/Help/html 168 | 169 | # Click-Once directory 170 | publish/ 171 | 172 | # Publish Web Output 173 | *.[Pp]ublish.xml 174 | *.azurePubxml 175 | # Note: Comment the next line if you want to checkin your web deploy settings, 176 | # but database connection strings (with potential passwords) will be unencrypted 177 | *.pubxml 178 | *.publishproj 179 | 180 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 181 | # checkin your Azure Web App publish settings, but sensitive information contained 182 | # in these scripts will be unencrypted 183 | PublishScripts/ 184 | 185 | # NuGet Packages 186 | *.nupkg 187 | # NuGet Symbol Packages 188 | *.snupkg 189 | # The packages folder can be ignored because of Package Restore 190 | **/[Pp]ackages/* 191 | # except build/, which is used as an MSBuild target. 192 | !**/[Pp]ackages/build/ 193 | # Uncomment if necessary however generally it will be regenerated when needed 194 | #!**/[Pp]ackages/repositories.config 195 | # NuGet v3's project.json files produces more ignorable files 196 | *.nuget.props 197 | *.nuget.targets 198 | 199 | # Microsoft Azure Build Output 200 | csx/ 201 | *.build.csdef 202 | 203 | # Microsoft Azure Emulator 204 | ecf/ 205 | rcf/ 206 | 207 | # Windows Store app package directories and files 208 | AppPackages/ 209 | BundleArtifacts/ 210 | Package.StoreAssociation.xml 211 | _pkginfo.txt 212 | *.appx 213 | *.appxbundle 214 | *.appxupload 215 | 216 | # Visual Studio cache files 217 | # files ending in .cache can be ignored 218 | *.[Cc]ache 219 | # but keep track of directories ending in .cache 220 | !?*.[Cc]ache/ 221 | 222 | # Others 223 | ClientBin/ 224 | ~$* 225 | *~ 226 | *.dbmdl 227 | *.dbproj.schemaview 228 | *.jfm 229 | *.pfx 230 | *.publishsettings 231 | orleans.codegen.cs 232 | 233 | # Including strong name files can present a security risk 234 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 235 | #*.snk 236 | 237 | # Since there are multiple workflows, uncomment next line to ignore bower_components 238 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 239 | #bower_components/ 240 | 241 | # RIA/Silverlight projects 242 | Generated_Code/ 243 | 244 | # Backup & report files from converting an old project file 245 | # to a newer Visual Studio version. Backup files are not needed, 246 | # because we have git ;-) 247 | _UpgradeReport_Files/ 248 | Backup*/ 249 | UpgradeLog*.XML 250 | UpgradeLog*.htm 251 | ServiceFabricBackup/ 252 | *.rptproj.bak 253 | 254 | # SQL Server files 255 | *.mdf 256 | *.ldf 257 | *.ndf 258 | 259 | # Business Intelligence projects 260 | *.rdl.data 261 | *.bim.layout 262 | *.bim_*.settings 263 | *.rptproj.rsuser 264 | *- [Bb]ackup.rdl 265 | *- [Bb]ackup ([0-9]).rdl 266 | *- [Bb]ackup ([0-9][0-9]).rdl 267 | 268 | # Microsoft Fakes 269 | FakesAssemblies/ 270 | 271 | # GhostDoc plugin setting file 272 | *.GhostDoc.xml 273 | 274 | # Node.js Tools for Visual Studio 275 | .ntvs_analysis.dat 276 | node_modules/ 277 | 278 | # Visual Studio 6 build log 279 | *.plg 280 | 281 | # Visual Studio 6 workspace options file 282 | *.opt 283 | 284 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 285 | *.vbw 286 | 287 | # Visual Studio LightSwitch build output 288 | **/*.HTMLClient/GeneratedArtifacts 289 | **/*.DesktopClient/GeneratedArtifacts 290 | **/*.DesktopClient/ModelManifest.xml 291 | **/*.Server/GeneratedArtifacts 292 | **/*.Server/ModelManifest.xml 293 | _Pvt_Extensions 294 | 295 | # Paket dependency manager 296 | .paket/paket.exe 297 | paket-files/ 298 | 299 | # FAKE - F# Make 300 | .fake/ 301 | 302 | # CodeRush personal settings 303 | .cr/personal 304 | 305 | # Python Tools for Visual Studio (PTVS) 306 | __pycache__/ 307 | *.pyc 308 | 309 | # Cake - Uncomment if you are using it 310 | # tools/** 311 | # !tools/packages.config 312 | 313 | # Tabs Studio 314 | *.tss 315 | 316 | # Telerik's JustMock configuration file 317 | *.jmconfig 318 | 319 | # BizTalk build output 320 | *.btp.cs 321 | *.btm.cs 322 | *.odx.cs 323 | *.xsd.cs 324 | 325 | # OpenCover UI analysis results 326 | OpenCover/ 327 | 328 | # Azure Stream Analytics local run output 329 | ASALocalRun/ 330 | 331 | # MSBuild Binary and Structured Log 332 | *.binlog 333 | 334 | # NVidia Nsight GPU debugger configuration file 335 | *.nvuser 336 | 337 | # MFractors (Xamarin productivity tool) working folder 338 | .mfractor/ 339 | 340 | # Local History for Visual Studio 341 | .localhistory/ 342 | 343 | # BeatPulse healthcheck temp database 344 | healthchecksdb 345 | 346 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 347 | MigrationBackup/ 348 | 349 | # Ionide (cross platform F# VS Code tools) working folder 350 | .ionide/ 351 | 352 | dist 353 | .parcel-cache 354 | -------------------------------------------------------------------------------- /.husky/pre-push: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | npm run readme 5 | git add README.md PROGRAM.md 6 | 7 | if [ -n "$(git diff --cached --exit-code)" ]; then 8 | git commit -m "Documentation updated" 9 | else 10 | echo "No changes to the documentation needed"; 11 | fi -------------------------------------------------------------------------------- /.posthtmlrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": { 3 | "posthtml-include": { 4 | "root": "./presentations" 5 | } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "tabWidth": 2, 3 | "semi": true, 4 | "singleQuote": true, 5 | "printWidth": 100, 6 | "endOfLine": "auto" 7 | } 8 | -------------------------------------------------------------------------------- /.vscode/reveal.code-snippets: -------------------------------------------------------------------------------- 1 | { 2 | "Animated left-aligned paragraph": { 3 | "scope": "html", 4 | "prefix": "pl", 5 | "body": ["

$1

", "$2"], 6 | "description": "Paragraph witn animation and left alignment" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /DONATIONS.MD: -------------------------------------------------------------------------------- 1 | # This is the donation list 2 | 3 | | Type | Amount | Target | 4 | | ---- | ------ | ---------------------------------- | 5 | | - | 279 | [Happy Meal for Orphans (2x)](https://t.me/toisamyibabich/845) | 6 | | + | 210 | Own Budget 7 | | - | 14070 | Medcines | 8 | | + | 900 | Own budget | 9 | | + | 900 | Own budget | 10 | | + | 12941 | External donation (Performance Talk) | 11 | | + | 200 | External donation (from tg) | 12 | | - | 5000 | [Oksana Savchenko SSO](https://www.facebook.com/semyz.larisa/posts/pfbid02mBFmpKjL1tJktwbU3Jkm1VP1hVTtL5Zwa23hbWas5cyGnjm9nPTVAA6r4Jk2FpjAl) | 13 | | + | 2415,70 | Own budget | 14 | | + | 2584,30 | From Open Source Collective | 15 | | - | 1000 | [ForPPO](https://dou.ua/forums/topic/41136/) | 16 | | + | 1000 | | 17 | | - | 1750 | [Jar for WASP wheels](https://send.monobank.ua/jar/6AVk2esdT2) | 18 | | + | 1750 | Finishin jar to 10k | 19 | | - | 6200 | [Jar for WASP wheels](https://send.monobank.ua/jar/6AVk2esdT2) | 20 | | + | 6200 | Donations | 21 | | - | 2050 | [Jar for WASP wheels](https://send.monobank.ua/jar/6AVk2esdT2) | 22 | | + | 2050 | Initial commit | 23 | | - | 2350 | [WASP](https://www.facebook.com/100001374307947/posts/pfbid02wrqNMRuB4jZtEncD1XaivNfQteUD8b3wjkcQxuMETzwJ46yAiXFcA33oZmxBHCWtl/) | 24 | | + | 2350 | Doubling previous | 25 | | + | 2350 | Talk about Web Tools | 26 | | - | 5288 | [Prytula Foundation](https://prytulafoundation.org/) | 27 | | + | 2644 | Doubling previous | 28 | | + | 150 | Pet Project for Fun and Profit 30/08 | 29 | | + | 2494 | Pet Project for Fun and Profit 30/08 | 30 | | - | 4000 | [DOU PD2](https://dou.ua/forums/topic/39602/) | 31 | | + | 100 | | 32 | | + | 1950 | Doubling previous | 33 | | + | 1950 | Pet Project for Fun and Profit (18/08) | 34 | | - | 5766 | [For SSO Boghuns](https://docs.google.com/spreadsheets/u/0/d/1Qt6JVvXZLArcWrDe0L0-rX1QM_SJ4ERNW4CHzttqPc4/htmlview#gid=0) | 35 | | + | 2883 | Doubling previous | 36 | | + | 2883 | Pet Project for Fun and Profit (11/08)| 37 | | - | 619,19 | [ArmySOS](https://armysos.com.ua/) | 38 | | + | 619,19 | | 39 | | - | 214 | ComeBackAlive | 40 | | + | 214 | | 41 | | - | 1000 | ComeBackAlive | 42 | | + | 1000 | | 43 | | - | 400 | ComeBackAlive | 44 | | + | 200 | | 45 | | + | 200 | | 46 | | - | 750 | ComeBackAlive | 47 | | + | 750 | | 48 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![StandsWithUkraine](https://raw.githubusercontent.com/Drag13/drag13.github.io/development/swu.PNG)](https://savelife.in.ua/en/donate/) 2 | 3 | # React For Beginners - Free Course by Itera 4 | 5 | ## [Watch on YouTube](https://www.youtube.com/channel/UCg-txtmOEQ8BniR8008O1mA) 6 | 7 | ## About 8 | 9 | **DISCLAIMER:** 10 | All requests to "remove politics" will be removed completely without any comments. If you have another opinion - just skip this course. 11 | 12 | This repo dedicated to the course "React for Beginners". The course was created to support Ukraine 🇺🇦 and Ukrainians in the war against russia. It's completely free and open-sourced. Feel free to contribute or make any relevant suggestions. 13 | 14 | - Full program is [here](PROGRAM.md) 15 | - Presentations can be found here - [https://drag13.io/react-learning-course-short/react-intro](https://drag13.io/react-learning-course-short/react-intro) where is the name of the lesson 16 | - Changelog is [here](CHANGELOG.md) 17 | - Video - published [here](https://www.youtube.com/channel/UCg-txtmOEQ8BniR8008O1mA) 18 | - Technical details are [here](devlog.md) 19 | 20 | ## Roadmap 21 | 22 | * Course program - done ✅ 23 | * Repository setup - done ✅ 24 | * Prepare materials - done ✅ 25 | * On-line - done ✅ 26 | * Retrospective - done ✅ 27 | * Updates - done ✅ 28 | * Second iteration - done ✅ 29 | 30 | ## PreRequisites 31 | 32 | Basic knowledge with HTML/CSS/JS 33 | 34 | Self check: 35 | 36 | - Example of the block element, how to draw a button 37 | - What is the width of block element, how to center element (vertically and horizontally) 38 | - What does `.map` returns, how to sum all values in array 39 | 40 | Basic knowledge with GIT: 41 | 42 | Self Check: 43 | 44 | - How to create new repository, how to push to remote 45 | 46 | Existing account at [https://github.com](https://github.com) 47 | Installed [Node.JS](https://nodejs.org/en/) with NPM 48 | Installed [VsCode](https://code.visualstudio.com/) 49 | 50 | ## Program summary 51 | 52 | ### 0: [What is React](https://youtu.be/fQ_UNyQBiqg) 53 | 54 | ### 1: [A New Project With Create-React-App](https://youtu.be/2r1TW9yPhlQ) 55 | 56 | ### 2: [My first react app with Vite](https://youtu.be/J_826v3GuCE) 57 | 58 | ### 3: [What is React Component](https://youtu.be/BPIeZqomYQw) 59 | 60 | ### 4: [React and Props](https://youtu.be/1gLLa4fJ1JQ) 61 | 62 | ### 5: [React and Hooks](https://youtu.be/6AHDZGumKZg) 63 | 64 | ### 6: [Building React Component](https://drag13.io/react-learning-course-short/react-new-component) 65 | 66 | ### 7: [React - From CSS to CSSinJS](https://www.youtube.com/watch?v=8al4xMhWWCE) 67 | 68 | ### 8: [Managing State in React](https://youtu.be/2KTqbf31cLw) 69 | 70 | ### 9: [Forms - Default Way and React-Hook-Forms](https://youtu.be/gwrMDwYLIWs) 71 | 72 | ### 10: [Routing in React](https://youtu.be/D0Fkm63FoSY) 73 | 74 | ### 11: [React and Network](https://youtu.be/Tm6l612v2v0) 75 | 76 | ### 12: [UI Libraries](https://youtu.be/4Dsgzk-GuX8) 77 | 78 | ### 13: [Testing Your Code](https://youtu.be/ASI73nQ9zP8) 79 | 80 | ### 14: [TypeScript For Beginners](https://youtu.be/ND-XaEQ4VSk) 81 | 82 | ### 15: [Useful tools for the web developer](https://drag13.io/react-learning-course-short/web-tools) 83 | 84 | ### 16: [SOLID & React](https://youtube.com/live/1D80PMHEBa0) 85 | 86 | ## Donations 87 | 88 | All donations are highly welcomed. You can donate any amount to the [National Bank of Ukraine directly](https://bank.gov.ua/en/news/all/natsionalniy-bank-vidkriv-spetsrahunok-dlya-zboru-koshtiv-na-potrebi-armiyi) or to the well known [charity fund Come Back Alive](https://www.comebackalive.in.ua/donate). 89 | 90 | Feel free to contact me directly if any question 91 | 92 | ## Sponsors 93 | 94 | [![](/presentations/shared/imgs/Itera-logo-white-fuchsia.jpg)](itera.com) 95 | 96 | ## Information support 97 | 98 | [![beerjs](./presentations/shared/imgs/39900370_1138320566319759_9157901823137284096_n.jpg)](https://t.me/beerJSZhytomyr) 99 | 100 | [![node.recipes](./presentations/shared/imgs/njsr.png)](http://node.recipes/) 101 | 102 | [![Babich Lviv CSS](./presentations/shared/imgs/babich.jpg)](https://t.me/toisamyibabich) -------------------------------------------------------------------------------- /SUMMARY.md: -------------------------------------------------------------------------------- 1 | # ReactJs для початківців від Віталія Рубана та Itera 2 | 3 | ## Вступ 4 | 5 | - [Що таке Реакт](https://youtu.be/fQ_UNyQBiqg) 6 | - [Перший застосунок на React](https://youtu.be/J_826v3GuCE) 7 | 8 | ## Компоненти в ReactJs 9 | 10 | - [Компонентний підхід в ReactJs](https://www.youtube.com/live/BPIeZqomYQw?feature=share) 11 | - [Що таке Props](https://youtu.be/1gLLa4fJ1JQ) 12 | - [Як поділити застосунок на компоненти](https://youtu.be/nKAkuk1Lgzw) 13 | 14 | ## Хуки 15 | 16 | - [Хуки - огляд](https://www.youtube.com/live/6AHDZGumKZg?feature=share) 17 | - [React, useContext та кастомні хуки](https://youtu.be/ifWtsE80-Ro) 18 | 19 | ## Навігація 20 | 21 | - [SPA та React](https://www.youtube.com/live/D0Fkm63FoSY?feature=share) 22 | - [React router DOM - 1](https://youtu.be/0mNV4VaI6w0) 23 | - [React router DOM - 2](https://youtu.be/VaDQN0pB5yw) 24 | - [React Router - код рев'ю](https://youtu.be/HKr8hpPbxd4) 25 | 26 | ## Керування станом 27 | 28 | - [Інструменти керування станом](https://www.youtube.com/live/2KTqbf31cLw?feature=share) 29 | - [React Redux - 1](https://youtu.be/WnBHLx7R4iU) 30 | - [React Redux - 2](https://youtu.be/WnBHLx7R4iU) 31 | 32 | ## Форми 33 | 34 | - [React та форми](https://www.youtube.com/live/gwrMDwYLIWs?feature=share) 35 | - [React Hook Form](https://youtu.be/_-Xe0-Nyg3Q) 36 | 37 | ## Стилізація 38 | 39 | - [React та CSS](https://youtu.be/7qtAR-YCEuE) 40 | - [Від звичайного CSS до CSSinJS](https://www.youtube.com/live/8al4xMhWWCE?feature=share) 41 | 42 | ## Тестування 43 | 44 | - [Тестування та Jest](https://www.youtube.com/live/ASI73nQ9zP8?feature=share) 45 | 46 | ## Інше 47 | 48 | - [Три популярні помилки початківців](https://youtu.be/aCPuRagpCCI) 49 | - [Ні похідним даним у state](https://youtu.be/TioGnP_62oA) 50 | - [React, Аутентифікація, Firebase](https://youtu.be/v30DUkpgPLk) 51 | - [Деплоїмо React на GitHub pages](https://youtu.be/Ll6y3y_wYVU) 52 | - [Пишемо гру на React](https://youtube.com/live/K9cLqD5Gy7c) 53 | -------------------------------------------------------------------------------- /devlog.md: -------------------------------------------------------------------------------- 1 | # Technical points 2 | 3 | The course is built with: 4 | 5 | - [Reveal.js](https://revealjs.com/) - presentation 6 | - [MustacheJs](https://github.com/janl/mustache.js/) - rendering documentation 7 | - [Parcel](https://parceljs.org/docs/) - building and bundling presentation 8 | 9 | ## Install 10 | 11 | ```cmd 12 | npm ci 13 | ``` 14 | 15 | ## Start the presentation locally 16 | 17 | If you want to start particular presentation - execute the next command where `` should be equal to the lesson name 18 | 19 | ```cmd 20 | npm start 21 | ``` 22 | 23 | Then open [http://localhost:1234/](http://localhost:1234/) 24 | 25 | ## How to update documentation 26 | 27 | All course related data should be stored in the `course.json` file. Updates should be done in the pre push phase **automatically**, with the help of Husky. To update documentation **manually** please execute the next command 28 | 29 | ```cmd 30 | npm run readme 31 | ``` 32 | 33 | If you need to tweak the documentation - check the `_templates` folder 34 | 35 | ## How to add new presentation 36 | 37 | - Add new lecture to the `course.json` 38 | - Run `npm run scaffold` command without arguments 39 | 40 | ## Deployment 41 | 42 | Deployment is automated, appears after the push to the master branch and done with help [github-pages-deploy-action](https://github.com/JamesIves/github-pages-deploy-action) 43 | 44 | ## Encoding 45 | 46 | Spotted that parcel or reveal doesn't like a lot of `<` `>` symbols - the code is completely messed. `.replaceAll('>','>')` can fix this. Check `encode.js` file for details. Use it with `npm encode`. 47 | -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | react, reactjs, реакт, реактджс, веб розрозробка, українською, навчання, learning, web, webdev, juniors, початківцям, javascript -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-learning-course-short", 3 | "version": "1.0.0", 4 | "description": "This repo dedicated to the course \"React for Beginners\". The course was created to support Ukraine 🇺🇦 and Ukrainians in the war against russia. It's completely free and open-sourced.", 5 | "private": false, 6 | "scripts": { 7 | "start": "node ./tools/start", 8 | "build": "parcel build ./presentations/**/index.html --public-url https://drag13.io/react-learning-course-short", 9 | "readme": "node tools/docs.js", 10 | "scaffold": "node tools/scaffold.js", 11 | "encode": "node tools/encode.js", 12 | "prepare": "husky install" 13 | }, 14 | "repository": { 15 | "type": "git", 16 | "url": "git+https://github.com/Drag13/react-learning-course-short.git" 17 | }, 18 | "keywords": [ 19 | "react", 20 | "tutorial", 21 | "courses", 22 | "mobx", 23 | "react-router" 24 | ], 25 | "author": "drag13", 26 | "license": "GNU", 27 | "bugs": { 28 | "url": "https://github.com/Drag13/react-learning-course-short/issues" 29 | }, 30 | "homepage": "https://github.com/Drag13/react-learning-course-short#readme", 31 | "dependencies": { 32 | "reveal.js": "^4.3.1" 33 | }, 34 | "devDependencies": { 35 | "gh-pages": "^3.2.3", 36 | "mustache": "^4.2.0", 37 | "parcel": "^2.4.0", 38 | "posthtml-doctype": "^1.1.1", 39 | "posthtml-include": "^1.7.3" 40 | }, 41 | "optionalDependencies": { 42 | "husky": "^8.0.1", 43 | "prettier": "^2.6.2" 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /presentations/about/body.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

About me

4 |
    5 |
  • Vitalii Ruban
  • 6 |
  • Head of Front-End department in Itera 8 |
  • 9 |
  • Member of the program committee JsFest Ukraine 11 | 12 |
  • 13 |
  • Love #webperf and #websec
  • 14 |
  • 15 | LinkedIn, 16 | Twitter 17 |
  • 18 |
19 |
20 |
21 | 22 |
23 |

This course is for

24 | 25 |
    26 |
  • Beginners
  • 27 |
  • Who already knowns HTML/CSS/JavaScript and
  • 28 |
  • Want to learn React
  • 29 |
30 |
31 | 32 |
33 |
34 |

We will talk about

35 | 36 | 64 |
65 | 66 |
67 | 68 |

And about

69 | 97 |
98 | 99 |
100 | 101 |

And about

102 | 137 |
138 | 139 |
-------------------------------------------------------------------------------- /presentations/about/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | React For Beginners - About the course 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 |
33 |
34 |
35 |

36 | Free React Course
37 | For Beginners 38 |

39 |
40 | 41 |
42 |

About the course

43 |

44 | React For Beginners by Itera 45 |

46 |
47 | 48 | 49 | 50 | 51 | 52 | 53 |
54 |

Useful links

55 | 65 |
66 |
67 |
68 | 69 | 70 | 71 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /presentations/pet-project/body.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

About me

4 |
    5 |
  • Vitalii Ruban
  • 6 |
  • 7 | Head of Front-End department in Itera 8 |
  • 9 |
  • 10 | Member of the program committee 11 | JsFest Ukraine 12 |
  • 13 |
  • Love #webperf and #websec
  • 14 |
  • 15 | LinkedIn, 21 | Twitter 22 |
  • 23 |
24 |
25 | 26 |
27 |

Part of my projects

28 | 29 |
    30 |
  • 31 | Chrome Extension - 32 | HabroSanitizer 34 | 35 | 36 | 37 |
  • 38 | 39 |
  • 40 | Visual Studio Extension - Angular 2 Web template - 30k downloads 41 |
  • 42 | 43 |
  • 44 | NPM project - IsNumberStrict 45 | 46 |
  • 47 |
  • And others ...
  • 48 |
49 |
50 | 51 |
52 |

However - much more of them was completely failed

53 |
54 |
55 | 56 |
57 |
58 |

What is a Pet Project

59 |
60 | 61 |
62 |

What is the main difference between Pet and Learning Project?

63 |
64 | 65 |
66 |

Main goal for a learning project - is to generate some knowledge

67 |
68 | 69 |
70 |

Main goal for a pet project - is to be fun ?

71 |
72 | 73 |
74 |

To me - main goal for a pet project - is to be both fun and released

75 |
76 | 77 |
78 |

Considering Pet project - always think about the release

79 |
80 | 81 |
82 |

And what about the demo project?

83 |

84 | A demo project is something that should demonstrates your skills. It should not be fun or 85 | released. Just show how good you are. 86 |

87 |
88 |
89 | 90 |
91 |
92 |

Hot to pick a theme

93 |
94 | 95 |
96 |

Good theme is super important

97 |

98 | The good theme will support you during the development and attract other people after the 99 | release 100 |

101 |
102 | 103 |
104 |

Never pick a PetProject that doesn't resonate with your heart

105 |
106 | 107 |
108 |

Two ways to pick a good theme

109 |
110 | 111 |
112 |

Something fun

113 |
114 | 115 |
116 |

Something painful

117 |
118 | 119 |
120 |

Example - the Shooter Log

121 |
122 |
123 | 124 |
125 |
126 |

Planing

127 |
128 | 129 |
130 |

Pet project is a race against yourself - until you get bored and tired

131 |
132 | 133 |
134 |

When doing pet project you will not have much time for a detailed planning

135 |
136 | 137 |
138 |
    139 |
  • Set one (maximum two) goals
  • 140 |
  • 141 | Categorize all work as must have (the solution is useless without such features) and nice to 142 | have 143 |
  • 144 |
  • Implement only must have features
  • 145 |
  • Note nice to have features somewhere just not to forget
  • 146 |
147 |
148 | 149 |
150 |

For the first 2-3 projects

151 | 152 |
    153 |
  • Not more than 16-20 hours
  • 154 |
  • Not more than 2-3 weeks
  • 155 |
156 |
157 | 158 |
159 |

Remember - something unplanned will happen, be prepared to X3 time

160 |
161 |
162 | 163 |
164 |

Time management tips

165 |
166 |

Set aside some dedicated time for your project

167 |

Pet project is not something unserious

168 |
169 | 170 |
171 |

Work on a regular basis

172 | 173 |

Better to work 2 hours every day, rather than 6 -> 2 -> 0

174 |
175 | 176 |
177 |

Focus on a one task at a time

178 |

Define -> Code -> Commit -> Relax

179 |
180 |
181 | 182 |
183 |
184 |

Pitfalls

185 |
186 | 187 |
188 |

Underestimates

189 | 190 |

Don't try to create a new FaceBook (at least from the first attempt)

191 |
192 | 193 |
194 |

Avoid adding non crucial features

195 | 196 |

Implement only the features that are absolutely needed for the project

197 |
198 | 199 |
200 |

Minimize infrastructure work

201 |

For the small projects, hard automatization takes more time than actually coding

202 |

Release should be you primary focus

203 |
204 | 205 |
206 |

Pausing

207 |

Work regularly and avoid pause

208 |
209 | 210 |
211 |

Abandoning

212 |

Abandoning project might lead you to the lack of self-esteem

213 |

Thus, first project better to be small and 99% doable

214 |
215 | 216 |
217 |

Turn it into work

218 |

Pet project HAVE to be fun, it should makes you proud and happy

219 |
220 |
221 | 222 |
223 |
224 |

What about:

225 |
226 | 227 |
228 |

Team work

229 |

The same except each new teammate increase general velocity for 0.8 then previous

230 |
231 | 232 |
233 |

Things after release

234 |

235 | Don't be shy to promote your project. You will gain extra experience, your first users and, 236 | might be even become a unicorn! 237 |

238 |
239 |
240 | -------------------------------------------------------------------------------- /presentations/pet-project/img/angularwebtemplate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Drag13/react-learning-course-short/748b4afdae58313d6cb12f2264c40d185db5aab5/presentations/pet-project/img/angularwebtemplate.png -------------------------------------------------------------------------------- /presentations/pet-project/img/projects.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Drag13/react-learning-course-short/748b4afdae58313d6cb12f2264c40d185db5aab5/presentations/pet-project/img/projects.png -------------------------------------------------------------------------------- /presentations/pet-project/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | React For Beginners - How to: Pet Project 9 | 10 | 11 | 12 | 13 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 |
34 |
35 |
36 |

37 | Free React Course
38 | For Beginners 39 |

40 |
41 | 42 |
43 |

Pet Project - For Fun and Profit

44 |

45 | For Beginners by Itera 46 |

47 |
48 | 49 |
50 |

Key Points

51 |
    52 |
  • What is a Pet Project
  • 53 |
  • Hot to pick a theme
  • 54 |
  • Planing
  • 55 |
  • Time management tips
  • 56 |
  • Pitfalls
  • 57 |
58 |
59 | 60 | 61 | 62 |
63 |

Takeaway

64 |
    65 |
  • Pet project is a great thing, but it requires efforts from your side
  • 66 |
  • Pet project = release
  • 67 |
  • For the first project - not more than 16 hours of work
  • 68 |
  • For the first release - only must have features
  • 69 |
  • Plan to work day by day
  • 70 |
71 |
72 | 73 | 74 | 75 |
76 |

Join me

77 | 78 | 79 |
80 | 86 | Twitter 87 |
88 | 89 |
90 | 96 | GitHub 97 |
98 | 99 |
100 |
101 | 102 | 103 | 104 | 109 | 110 | 111 | -------------------------------------------------------------------------------- /presentations/react-change-detection/body.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

What is change detection

4 |
5 | 6 |
7 |

Change detection - mechanism that tracks data changes and notifies appropriate listeners

8 |
9 | 10 |
11 |

As an example - look on the BtnWithCounter:

12 |
state = {counter: 0}
13 |
⬇️
14 |
Clicked 0 times
15 |
🖱️ Click
16 |
⬇️
17 |
setState({counter:1})
18 |
⬇️
19 |
Clicked 1 times
20 |
21 | 22 |
23 |

It looks very simple from the first sight but imagine:

24 |
    25 |
  • Rich data
  • 26 |
  • Rich user interaction
  • 27 |
  • Backend interaction
  • 28 |
29 |

🤯🤯🤯

30 |
31 |
32 |
33 |

Two ways to track changes

34 |
35 |
36 | 37 |
38 |

39 | Implicit change detection strategy 40 |

41 |
42 |
43 |

Angular way

44 |
 45 |             class CalculatorService {
 46 |     income: number;
 47 |     setIncome(newIncome){
 48 |         this.income = newIncome;
 49 |     }
 50 | }
 51 | 
 52 | 
53 |
 54 |     
{{income}}
55 |
56 |
57 | 58 |
59 |

60 | No special actions to inform framework that data changes 61 |

62 |
63 | 64 |
65 |

In Angular this works because of NgZone and patching all DOM 67 | events 68 |

69 | 70 |

Whenever something happens - click, network, input - Angular starts checking changes

71 |
72 | 73 |
74 | 75 |
76 |
77 |

React uses another way - explicit

78 |
79 | 80 |
81 |

With explicit way you have to somehow notify framework that data changes

82 |

Usually this achieved with using special methods

83 |
84 | 85 |
86 |

For class based components - setState

87 |
setState({})
88 |
89 | 90 |
91 |

For functional components - useState hook

92 |
const [counter, setCounter] = useState({});
 93 | setCounter(1);
 94 |         
95 |
96 | 97 |
98 |

Ok, we notified react that data changed. What next?

99 |
100 | 101 |
102 |
    103 |
  • After the notification React will recalculate all components which 104 |
      105 |
    • changed props
    • 106 |
    • changed state
    • 107 |
    • all child functional components for p.1 and p2
    • 108 |
    109 |
  • 110 |
  • Build new Virtual DOM based on the results
  • 111 |
  • Find the diff
  • 112 |
  • Updates real DOM to match new Virtual DOM
  • 113 |
114 |
115 |
116 |
117 |
118 |

Typical mistakes

119 |
120 | 121 |
122 |

123 | 💔 Ignoring state when data is subject to change 124 |

125 | 126 |
const data = { counter: 0 };
127 | const MyBtn = () => {
128 |     return <button onClick={() => (data.counter += 1)}>
129 |         {data.counter}
130 |     </button>;
131 | };
132 |             
133 |
134 | 135 |
136 |

💔 Mutating state directly, without special methods

137 |
const MyBtn = () => {
138 |     const [data] = useState({ counter: 0 });
139 |     return <button onClick={() => (data.counter += 1)}>
140 |         {data.counter}
141 |     </button>;
142 | };
143 |           
144 |
145 | 146 |
147 |

💔 Also applicable for Class based components

148 |
class MyBtn extends Component {
149 | state = { counter: 0 };
150 | render() {
151 |     return (
152 |         <button onClick={() => (this.state.counter += 1)}>
153 |             {this.state.counter}
154 |         </button>
155 |     );}
156 | }
157 |           
158 |
159 | 160 |
161 |

💔 Mixing approach

162 |

163 | Using this.setState() with functional components 164 |

165 | 166 |

167 | Using useState with class-based components 168 |

169 |
170 | 171 |
172 |

If you data is supposed to change - use state and don't mutate it directly

173 | 174 |
    175 |
  • setState for class components
  • 176 |
  • useSate for functional components
  • 177 |
178 |
179 | 180 |
-------------------------------------------------------------------------------- /presentations/react-change-detection/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | React For Beginners - Tracking Changes 9 | 10 | 11 | 12 | 13 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 |
34 |
35 |
36 |

37 | Free React Course
38 | For Beginners 39 |

40 |
41 | 42 |
43 |

L5 - Tracking Changes

44 |

45 | React For Beginners by Itera 46 |

47 |
48 | 49 |
50 |

Key Points

51 |
    52 |
  • What is change detection
  • 53 |
  • Implicit change detection with Angular
  • 54 |
  • Explicit change detection with React
  • 55 |
  • How it works in React (simplified)
  • 56 |
  • Typical mistakes
  • 57 |
58 |
59 | 60 | 61 | 62 |
63 |

Useful links

64 | 69 |
70 |
71 |
72 | 73 | 74 | 75 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /presentations/react-components/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | React For Beginners - What is React Component 9 | 10 | 11 | 12 | 13 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 |
34 |
35 |
36 |

37 | Free React Course
38 | For Beginners 39 |

40 |
41 | 42 |
43 |

L2 - What is React Component

44 |

45 | React For Beginners by Itera 46 |

47 |
48 | 49 |
50 |

Key Points

51 |
    52 |
  • What is the component
  • 53 |
  • Why to use the components
  • 54 |
  • Component types
  • 55 |
  • How to pass data into the component
  • 56 |
  • How to get data from the component
  • 57 |
  • Component lifecycle
  • 58 |
59 |
60 | 61 | 62 | 63 |
64 |

Takeaway

65 |
    66 |
  • Component - an independent unit of code that encapsulate single, logical functionality
  • 67 |
  • Components might be class based or functional
  • 68 |
  • Components divided into "smart" and "dumb"
  • 69 |
  • Components have lifecycle you can hook into
  • 70 |
71 |
72 | 73 |
74 |

Home task

75 |
    76 |
  • Split your APP into the logical components
  • 77 |
  • Mix Class Based and Functional approach to get more practice in both
  • 78 |
  • Use props only to pass the data to your components
  • 79 |
80 |
81 | 82 |
83 |

Useful links

84 | 89 |
90 | 91 |
92 |

Join me

93 | 94 | 95 |
96 | 102 | Twitter 103 |
104 | 105 |
106 | 112 | GitHub 113 |
114 | 115 |
116 |
117 | 118 | 119 | 120 | 125 | 126 | 127 | -------------------------------------------------------------------------------- /presentations/react-cra/body.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

Starting new React project from scratch using create-react-app

4 |
5 | 6 |
7 | 8 |

Open VsCode's console with CTRL+`

9 |
 10 |             npx create-react-app myproj --template typescript
 11 |             
 12 |         
13 |
14 | 15 |
16 |

Let's read it

17 | 18 |
 19 |             npx create-react-app myproj --template typescript
 20 |         
21 |
    22 |
  • 23 | npx - the npm tool that allow you to download npm package and execute it 24 |
  • 25 |
  • 26 | create-react-app - npm package from Meta (Facebook) that creates new project 27 | with default structure and all dependencies 28 |
  • 29 |
  • 30 | myproj - name of the project 31 |
  • 32 |
  • 33 | --template typescript - template to use for project scaffolding. We will use 34 | template with typescript 35 |
  • 36 |
37 |
38 | 39 |
40 | Simply saying: 41 |

42 | Download the create-react-app package from npm and execute it with parameter template equals to 43 | typescript 44 |

45 |
46 | 47 |
48 |
49 |
50 |

Project structure

51 |
52 | 53 |
54 | create-react-app-project 55 |
56 | 57 |
58 |
 59 |             /public
 60 |     index.html          // root file to load
 61 |     manifest.json       // web app metadata
 62 | /src
 63 |     App.css             // styles related to the App component
 64 |     App.test.tsx        // tests related to the App component
 65 |     App.tsx             // App component
 66 |     index.css           // Global styles
 67 |     react-app-env.d.ts  // extra types
 68 |     reportWebVitals.ts  // performance metrics report
 69 |     setupTests.ts       // test configuration
 70 | .gitignore              // git ignore configuration
 71 | package-lock.json       // all project dependencies
 72 | package.json            // project configuration file
 73 | tsconfig.json           // typescript configuration file
 74 |             
 75 |         
76 | 77 |
78 |
79 | 80 |
81 |
82 |

Files you need to know

83 |
84 | 85 |
86 |

Index.html

87 | 88 | react - index.html 89 |
90 | 91 |
92 |

index.tsx

93 | 94 | react - index.tsx 95 |
96 | 97 |
98 |

App.tsx

99 | 100 | react - app.tsx 101 |
102 | 103 |
104 |

App.test.tsx

105 | react - app.text.tsx 106 |
107 | 108 |
109 |

package.json

110 | react - package.json 111 |
112 |
113 | 114 |
115 |
116 |

Predefined commands

117 |
118 | 119 |
120 |

Start

121 |

Start application for local development

122 |
npm start
123 |
124 | 125 |
126 |

Test

127 |

Tests application

128 |
npm test
129 |
130 | 131 |
132 |

Build

133 |

Builds application for production - bundling, minification, etc

134 |
npm run build
135 |
136 |
137 |

Eject

138 |

Converts create-react-app to the regular front-end app

139 |
npm run eject
140 |

⚠️ It's one time action

141 |
142 |
143 | 144 |
145 |
146 |

Code examples

147 |
148 | 149 |
150 |
    151 |
  • Open the folder with VsCode
  • 152 |
  • Hit CTRL ~ to start terminal
  • 153 |
  • type npm start in the console
  • 154 |
155 |
156 | 157 |
158 | Default UI wit create-react-app 159 |
160 | 161 |
162 | Change code in the app.tsx and save file 163 |
164 |             import "./App.css";
165 | 
166 | function App() {
167 |     return (
168 |         <div className="App">
169 |             hello world
170 |         </div>;
171 |     );
172 | }
173 | 
174 | export default App;
175 |             
176 |         
177 | 178 |
179 |
180 |

Line by line

181 |
182 |             import "./App.css";
183 | 
184 | function App() {
185 |     return (
186 |         <div className="App">
187 |             hello world
188 |         </div>;
189 |     );
190 | }
191 | 
192 | export default App;
193 |         
194 |     
195 | 196 |
197 | 198 |
199 |

200 | Feel free to alter the code inside the div. You can add any other HTML tag you know or 201 | text. However, tags are not HTML - it's TSX 203 |

204 | 205 |

206 | After saving the file React will update the UI automatically. This called Hot Module Reloading 207 |

208 |
209 | 210 |
211 | React is able to render the data as well 212 | 213 |
214 |             import "./App.css";
215 | 
216 | const data = { greetings: "Hello World!" };
217 | 
218 | function App() {
219 |     return (
{data.greetings}
); 220 | } 221 | 222 | export default App; 223 |
224 |
225 |
226 |
227 | 228 |
229 | 230 |
231 |

Alternatives

232 |
233 | 234 |
235 |

Vite

236 | 237 |
npm init vite
238 |
239 | 240 |
241 |

Vite benefits

242 |
    243 |
  • Very fast HMR
  • 244 |
  • Less dependencies
  • 245 |
246 |
247 | 248 |
249 |

Vite cons

250 |
    251 |
  • Needs more tuning - like linters
  • 252 |
  • It's not a webpack
  • 253 |
254 |
255 |
-------------------------------------------------------------------------------- /presentations/react-cra/img/app-tsx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Drag13/react-learning-course-short/748b4afdae58313d6cb12f2264c40d185db5aab5/presentations/react-cra/img/app-tsx.png -------------------------------------------------------------------------------- /presentations/react-cra/img/create-react-app-project.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Drag13/react-learning-course-short/748b4afdae58313d6cb12f2264c40d185db5aab5/presentations/react-cra/img/create-react-app-project.png -------------------------------------------------------------------------------- /presentations/react-cra/img/create-react-app-ui-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Drag13/react-learning-course-short/748b4afdae58313d6cb12f2264c40d185db5aab5/presentations/react-cra/img/create-react-app-ui-example.png -------------------------------------------------------------------------------- /presentations/react-cra/img/index-html.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Drag13/react-learning-course-short/748b4afdae58313d6cb12f2264c40d185db5aab5/presentations/react-cra/img/index-html.png -------------------------------------------------------------------------------- /presentations/react-cra/img/index-tsx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Drag13/react-learning-course-short/748b4afdae58313d6cb12f2264c40d185db5aab5/presentations/react-cra/img/index-tsx.png -------------------------------------------------------------------------------- /presentations/react-cra/img/package-json.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Drag13/react-learning-course-short/748b4afdae58313d6cb12f2264c40d185db5aab5/presentations/react-cra/img/package-json.png -------------------------------------------------------------------------------- /presentations/react-cra/img/test-tsx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Drag13/react-learning-course-short/748b4afdae58313d6cb12f2264c40d185db5aab5/presentations/react-cra/img/test-tsx.png -------------------------------------------------------------------------------- /presentations/react-cra/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | React For Beginners - A New Project With Create-React-App 9 | 10 | 11 | 12 | 13 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 |
34 |
35 |
36 |

37 | Free React Course
38 | For Beginners 39 |

40 |
41 | 42 |
43 |

L1 - A New Project With Create-React-App

44 |

45 | React For Beginners by Itera 46 |

47 |
48 | 49 |
50 |

Key Points

51 |
    52 |
  • Starting new React project from scratch using create-react-app
  • 53 |
  • Project structure
  • 54 |
  • Files you need to know
  • 55 |
  • Predefined commands
  • 56 |
  • Code examples
  • 57 |
  • Alternatives
  • 58 |
59 |
60 | 61 | 62 | 63 |
64 |

Takeaway

65 |
    66 |
  • Creating new React project is very simple
  • 67 |
  • Use npm start to start the an app locally or npm run build to build it for release
  • 68 |
69 |
70 | 71 |
72 |

Home task

73 |
    74 |
  • Create new repository at GitHub and name it react-for-beginners-itera
  • 75 |
  • Select .gitignore from VisualStudio
  • 76 |
  • Clone repo locally using git clone
  • 77 |
  • Initialize new react application using npx create-react-app my-page --template typescript
  • 78 |
  • Create a new JSON with next information:
  • 79 |
  • First Name
  • 80 |
  • Short biography
  • 81 |
  • Public contacts
  • 82 |
  • Use React to display data from the JSON on the page
  • 83 |
  • Commit and push the results with git add ., git commit -m "Initial commit", git push
  • 84 |
85 |
86 | 87 |
88 |

Useful links

89 | 94 |
95 | 96 |
97 |

Join me

98 | 99 | 100 |
101 | 107 | Twitter 108 |
109 | 110 |
111 | 117 | GitHub 118 |
119 | 120 |
121 |
122 | 123 | 124 | 125 | 130 | 131 | 132 | -------------------------------------------------------------------------------- /presentations/react-forms/code/default-form.tsx: -------------------------------------------------------------------------------- 1 | import React, { ChangeEvent, Component, FormEvent, PureComponent } from "react"; 2 | import { createRoot } from "react-dom/client"; 3 | 4 | class MyForm extends PureComponent { 5 | state = { email: "test" }; 6 | render() { 7 | return ( 8 |
9 |

My form

10 | 17 | 18 |
19 | ); 20 | } 21 | 22 | private _handleSubmit = (e: FormEvent) => { 23 | e.preventDefault(); 24 | console.log(`Form state`, this.state); 25 | }; 26 | 27 | private _handleChange = (e: ChangeEvent) => 28 | this.setState({ name: e.target.value }); 29 | } 30 | 31 | // Application 32 | class App extends Component { 33 | state = { authenticated: true }; 34 | render() { 35 | return ; 36 | } 37 | } 38 | 39 | const container = document.getElementById("root"); 40 | createRoot(container!).render( 41 | 42 | 43 | 44 | ); 45 | -------------------------------------------------------------------------------- /presentations/react-forms/code/yup.tsx: -------------------------------------------------------------------------------- 1 | import React, { memo, PureComponent, useCallback } from 'react'; 2 | import { createRoot } from 'react-dom/client'; 3 | import { useForm } from 'react-hook-form'; 4 | 5 | import { BaseSchema, InferType, object, string } from 'yup'; 6 | 7 | const validationSchema = object({ 8 | email: string().required().email(), 9 | password: string().required().min(6).max(12), 10 | }); 11 | 12 | type FormData = InferType; 13 | 14 | const useYup = (schema: BaseSchema) => { 15 | return useCallback( 16 | async (data: T) => { 17 | try { 18 | const values = await schema.validate(data, { abortEarly: false }); 19 | return { values, errors: {} }; 20 | } catch (yupErrors: any) { 21 | const errors = yupErrors.inner.reduce( 22 | (acc, v: { message: string; path: string; type: string }) => { 23 | acc[v.path] = { type: v.type ?? 'validation', message: v.message }; 24 | return acc; 25 | }, 26 | {} as Record 27 | ); 28 | return { values: {}, errors }; 29 | } 30 | }, 31 | [schema] 32 | ); 33 | }; 34 | 35 | const MyForm = memo(() => { 36 | const validations = useYup(validationSchema); 37 | const { register, handleSubmit, formState } = useForm({ 38 | resolver: validations, 39 | }); 40 | 41 | const { errors } = formState; 42 | 43 | return ( 44 |
console.log(d))} name="myform"> 45 |

My form

46 | 47 | {errors?.email && errors?.email?.message} 48 |

49 | 50 |

51 |
52 | ); 53 | }); 54 | 55 | class App extends PureComponent { 56 | render() { 57 | return ( 58 | <> 59 | 60 | 61 | ); 62 | } 63 | } 64 | 65 | const container = document.getElementById('root'); 66 | createRoot(container!).render( 67 | 68 | 69 | 70 | ); 71 | -------------------------------------------------------------------------------- /presentations/react-forms/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | React For Beginners - Forms - Default Way and React-Hook-Forms 9 | 10 | 11 | 12 | 13 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 |
34 |
35 |
36 |

37 | Free React Course
38 | For Beginners 39 |

40 |
41 | 42 |
43 |

L7 - Forms - Default Way and React-Hook-Forms

44 |

45 | React For Beginners by Itera 46 |

47 |
48 | 49 |
50 |

Key Points

51 |
    52 |
  • React Forms - default way
  • 53 |
  • Building your first form with React Hook Form
  • 54 |
  • Validation with Yup
  • 55 |
56 |
57 | 58 | 59 | 60 | 61 | 62 |
63 |

Useful links

64 | 69 |
70 | 71 |
72 |

Join me

73 | 74 | 75 |
76 | 82 | Twitter 83 |
84 | 85 |
86 | 92 | GitHub 93 |
94 | 95 |
96 |
97 | 98 | 99 | 100 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /presentations/react-hooks/code/useReducer.tsx: -------------------------------------------------------------------------------- 1 | import { FC, useCallback, useReducer } from "react"; 2 | import ReactDOM from "react-dom/client"; 3 | import "./index.css"; 4 | import reportWebVitals from "./reportWebVitals"; 5 | 6 | enum ActionType { 7 | SET_NAME, 8 | INCREMENT_SALARY, 9 | FIRE, 10 | } 11 | 12 | type NumberAction = { 13 | type: TAction; 14 | payload: number; 15 | }; 16 | 17 | type EmptyAction = { 18 | type: TAction; 19 | }; 20 | 21 | type Action = 22 | | NumberAction 23 | | EmptyAction; 24 | 25 | type State = { 26 | salary: number; 27 | fired: boolean; 28 | name: string; 29 | }; 30 | 31 | const stateReducer = (state: State, action: Action): State => { 32 | switch (action.type) { 33 | case ActionType.FIRE: 34 | return { ...state, salary: 0, fired: true }; 35 | case ActionType.INCREMENT_SALARY: 36 | return { ...state, salary: state.salary + action.payload }; 37 | } 38 | }; 39 | 40 | const defaultState = { name: "Super Employee", salary: 100, fired: false }; 41 | 42 | const App = () => { 43 | const [state, dispatch] = useReducer(stateReducer, defaultState); 44 | const { name, salary, fired } = state; 45 | const fire = useCallback(() => dispatch({ type: ActionType.FIRE }), []); 46 | const incrementSalary = useCallback( 47 | () => dispatch({ type: ActionType.INCREMENT_SALARY, payload: 50 }), 48 | [] 49 | ); 50 | 51 | return fired ? ( 52 | 53 | ) : ( 54 | 60 | ); 61 | }; 62 | 63 | const Fired: FC<{ employeeName: string }> = ({ employeeName }) => ( 64 |
{employeeName} is fired
65 | ); 66 | 67 | type EmployeeInterfaceProps = { 68 | employeeName: string; 69 | salary: number; 70 | onSalaryIncrement: () => void; 71 | onFired: () => void; 72 | }; 73 | 74 | const EmployeeInterface: FC = ({ 75 | employeeName, 76 | salary, 77 | onFired, 78 | onSalaryIncrement, 79 | }) => { 80 | return ( 81 |
82 |

Employee: {employeeName}

83 |
Salary: {salary}
84 | 87 | 90 |
91 | ); 92 | }; 93 | 94 | ReactDOM.createRoot(document.getElementById("root")!).render(); 95 | 96 | reportWebVitals(); 97 | -------------------------------------------------------------------------------- /presentations/react-hooks/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | React For Beginners - React and Hooks 9 | 10 | 11 | 12 | 13 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 |
34 |
35 |
36 |

37 | Free React Course
38 | For Beginners 39 |

40 |
41 | 42 |
43 |

L3 - React and Hooks

44 |

45 | React For Beginners by Itera 46 |

47 |
48 | 49 |
50 |

Key Points

51 |
    52 |
  • What is a hook
  • 53 |
  • Hook rules
  • 54 |
  • UseState
  • 55 |
  • Use Effect
  • 56 |
  • UseContext
  • 57 |
  • Custom hooks
  • 58 |
59 |
60 | 61 | 62 | 63 |
64 |

Takeaway

65 |
    66 |
  • Hook - a modern to API to functional components
  • 67 |
  • Most useful are useState, useEffect
  • 68 |
  • You can write your own hooks
  • 69 |
70 |
71 | 72 | 73 |
74 |

Useful links

75 | 80 |
81 | 82 |
83 |

Join me

84 | 85 | 86 |
87 | 93 | Twitter 94 |
95 | 96 |
97 | 103 | GitHub 104 |
105 | 106 |
107 |
108 | 109 | 110 | 111 | 116 | 117 | 118 | -------------------------------------------------------------------------------- /presentations/react-intro/img/old.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Drag13/react-learning-course-short/748b4afdae58313d6cb12f2264c40d185db5aab5/presentations/react-intro/img/old.png -------------------------------------------------------------------------------- /presentations/react-intro/img/pb24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Drag13/react-learning-course-short/748b4afdae58313d6cb12f2264c40d185db5aab5/presentations/react-intro/img/pb24.png -------------------------------------------------------------------------------- /presentations/react-intro/img/react-ang-vue-downloads-020422.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Drag13/react-learning-course-short/748b4afdae58313d6cb12f2264c40d185db5aab5/presentations/react-intro/img/react-ang-vue-downloads-020422.png -------------------------------------------------------------------------------- /presentations/react-intro/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | React For Beginners - What is React 9 | 10 | 11 | 12 | 13 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 |
34 |
35 |
36 |

37 | Free React Course
38 | For Beginners 39 |

40 |
41 | 42 |
43 |

L0 - What is React

44 |

45 | React For Beginners by Itera 46 |

47 |
48 | 49 |
50 |

Key Points

51 |
    52 |
  • The problem
  • 53 |
  • What is the React
  • 54 |
  • Three pillars of React
  • 55 |
  • React - advantages and disadvantages
  • 56 |
  • Why to learn React nowadays
  • 57 |
58 |
59 | 60 | 61 | 62 |
63 |

Takeaway

64 |
    65 |
  • React - JavaScript library for building user interfaces
  • 66 |
  • React uses JSX - HTMLish syntax to describe UI
  • 67 |
  • Data is the single source of truth
  • 68 |
69 |
70 | 71 | 72 |
73 |

Useful links

74 | 80 |
81 | 82 |
83 |

Join me

84 | 85 | 86 |
87 | 93 | Twitter 94 |
95 | 96 |
97 | 103 | GitHub 104 |
105 | 106 |
107 |
108 | 109 | 110 | 111 | 116 | 117 | 118 | -------------------------------------------------------------------------------- /presentations/react-network/code/fetch-axios.tsx: -------------------------------------------------------------------------------- 1 | import { createRoot } from "react-dom/client"; 2 | import Axios from "axios"; 3 | import { PureComponent, useEffect, useState } from "react"; 4 | 5 | const FREE_API_URL = 6 | "https://api.publicapis.org/entries?auth=null&cors=no&category=animals"; 7 | 8 | const axios = Axios.create(); 9 | axios.interceptors.response.use((response) => { 10 | console.log(`${response.request.responseURL}:${response.status}`); 11 | return response; 12 | }); 13 | 14 | const useFreeApi = () => { 15 | const [data, setApi] = useState({ errored: false, apis: [] }); 16 | useEffect(() => { 17 | const abort = new AbortController(); 18 | async function updateApi() { 19 | try { 20 | const { data } = await axios.get(FREE_API_URL, { 21 | signal: abort.signal, 22 | timeout: 1500, 23 | }); 24 | const { entries } = data; 25 | setApi({ errored: false, apis: entries }); 26 | } catch (error) { 27 | setApi({ errored: true, apis: [] }); 28 | } 29 | } 30 | updateApi(); 31 | 32 | return () => abort.abort(); 33 | }, []); 34 | return data; 35 | }; 36 | 37 | const FreeApi = () => { 38 | const { errored, apis } = useFreeApi(); 39 | return errored ? ( 40 |
Something went wrong
41 | ) : ( 42 |
    43 | {apis.map(({ API, Link }) => ( 44 |
  • 45 | {API}::{Link} 46 |
  • 47 | ))} 48 |
49 | ); 50 | }; 51 | 52 | class App extends PureComponent { 53 | render() { 54 | return ; 55 | } 56 | } 57 | 58 | const container = document.getElementById("root"); 59 | createRoot(container!).render(); 60 | -------------------------------------------------------------------------------- /presentations/react-network/code/fetch-cb.tsx: -------------------------------------------------------------------------------- 1 | import { createRoot } from "react-dom/client"; 2 | import { PureComponent } from "react"; 3 | 4 | const FREE_API_URL = 5 | "https://api.publicapis.org/entries?auth=null&cors=no&category=animals"; 6 | 7 | class FreeApi extends PureComponent { 8 | state = { errored: false, apis: [] }; 9 | 10 | async componentDidMount() { 11 | try { 12 | const rawData = await fetch(FREE_API_URL); 13 | const { entries } = await rawData.json(); 14 | this.setState({ ...this.state, apis: entries }); 15 | } catch (e) { 16 | this.setState({ errored: true, apis: [] }); 17 | } 18 | } 19 | 20 | render() { 21 | const { apis, errored } = this.state; 22 | return errored ? ( 23 |
Something went wrong
24 | ) : ( 25 |
    {apis.map(({ API, Link }) => ( 26 |
  • 27 | {API}::{Link} 28 |
  • 29 | ))} 30 |
31 | ); 32 | } 33 | } 34 | 35 | class App extends PureComponent { 36 | render() { 37 | return ; 38 | } 39 | } 40 | 41 | const container = document.getElementById("root"); 42 | createRoot(container!).render(); 43 | -------------------------------------------------------------------------------- /presentations/react-network/code/fetch-functional.tsx: -------------------------------------------------------------------------------- 1 | import { createRoot } from "react-dom/client"; 2 | import { PureComponent, useEffect, useState } from "react"; 3 | 4 | const FREE_API_URL = 5 | "https://api.publicapis.org/entries?auth=null&cors=no&category=animals"; 6 | 7 | const useFreeApi = () => { 8 | const [data, setApi] = useState({ errored: false, apis: [] }); 9 | useEffect(() => { 10 | async function updateApi() { 11 | try { 12 | const rawData = await fetch(FREE_API_URL); 13 | const { entries } = await rawData.json(); 14 | setApi({ errored: false, apis: entries }); 15 | } catch (error) { 16 | setApi({ errored: true, apis: [] }); 17 | } 18 | } 19 | updateApi(); 20 | }, []); 21 | return data; 22 | }; 23 | 24 | const FreeApi = () => { 25 | const { errored, apis } = useFreeApi(); 26 | return errored ? ( 27 |
Something went wrong
28 | ) : ( 29 |
    30 | {apis.map(({ API, Link }) => ( 31 |
  • 32 | {API}::{Link} 33 |
  • 34 | ))} 35 |
36 | ); 37 | }; 38 | 39 | class FreeApiCB extends PureComponent { 40 | state = { errored: false, apis: [] }; 41 | 42 | async componentDidMount() { 43 | try { 44 | const rawData = await fetch(FREE_API_URL); 45 | const { entries } = await rawData.json(); 46 | this.setState({ ...this.state, apis: entries }); 47 | } catch (e) { 48 | this.setState({ errored: true, apis: [] }); 49 | } 50 | } 51 | 52 | render() { 53 | const { apis, errored } = this.state; 54 | return errored ? ( 55 |
Something went wrong
56 | ) : ( 57 |
    58 | {apis.map(({ API, Link }) => ( 59 |
  • 60 | {API}::{Link} 61 |
  • 62 | ))} 63 |
64 | ); 65 | } 66 | } 67 | 68 | class App extends PureComponent { 69 | render() { 70 | return ; 71 | } 72 | } 73 | 74 | const container = document.getElementById("root"); 75 | createRoot(container!).render(); 76 | -------------------------------------------------------------------------------- /presentations/react-network/code/fetch.tsx: -------------------------------------------------------------------------------- 1 | import { FC, PureComponent, useEffect, useState } from "react"; 2 | import { createRoot } from "react-dom/client"; 3 | 4 | const FREE_API_URL = "https://api.publicapis.org/entries?auth=null&cors=no"; 5 | 6 | type Api = { api: string; link: string }; 7 | type Category = "animals" | "weather"; 8 | const useFreeApi = (category: Category = "animals"): Api[] => { 9 | const [apis, setResponse] = useState([]); 10 | 11 | useEffect(() => { 12 | async function fetchApi() { 13 | const URL = `${FREE_API_URL}&category=${category}`; 14 | const { entries } = await fetch(URL).then((x) => x.json()); 15 | setResponse(entries.map((e: any) => ({ api: e.API, link: e.Link }))); 16 | } 17 | 18 | fetchApi(); 19 | }, [category]); 20 | return apis; 21 | }; 22 | 23 | const FreeApi = () => { 24 | const api = useFreeApi("weather"); 25 | return api.length ? : <>"fetching"; 26 | }; 27 | 28 | const ApiList: FC<{ api: Api[] }> = ({ api }) => ( 29 | <> 30 | {api.map(({ api, link }) => ( 31 |
32 | {api}::{link} 33 |
34 | ))} 35 | 36 | ); 37 | 38 | class App extends PureComponent { 39 | render() { 40 | return ; 41 | } 42 | } 43 | 44 | const container = document.getElementById("root"); 45 | createRoot(container!).render(); 46 | -------------------------------------------------------------------------------- /presentations/react-network/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | React For Beginners - React and Network 9 | 10 | 11 | 12 | 13 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 |
34 |
35 |
36 |

37 | Free React Course
38 | For Beginners 39 |

40 |
41 | 42 |
43 |

L9 - React and Network

44 |

45 | React For Beginners by Itera 46 |

47 |
48 | 49 |
50 |

Key Points

51 |
    52 |
  • Fetch
  • 53 |
  • Axios
  • 54 |
  • Network and store
  • 55 |
  • Typical mistakes
  • 56 |
57 |
58 | 59 | 60 | 61 |
62 |

Takeaway

63 |
    64 |
  • Use fetch for a simple project and axios for middle/large
  • 65 |
  • Move logic out of component
  • 66 |
  • Don't ignore errors
  • 67 |
68 |
69 | 70 | 71 |
72 |

Useful links

73 | 78 |
79 | 80 |
81 |

Join me

82 | 83 | 84 |
85 | 91 | Twitter 92 |
93 | 94 |
95 | 101 | GitHub 102 |
103 | 104 |
105 |
106 | 107 | 108 | 109 | 114 | 115 | 116 | -------------------------------------------------------------------------------- /presentations/react-new-component/code/counter-cb.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { createRoot } from "react-dom/client"; 3 | import "./index.css"; 4 | 5 | type BtnWithCounterProps = { 6 | onAfterChange: (v: number) => void; 7 | }; 8 | 9 | class BtnWithCounter extends React.PureComponent { 10 | state = { counter: 0 }; 11 | 12 | render() { 13 | const { counter } = this.state; 14 | return ( 15 | 18 | ); 19 | } 20 | 21 | handleClick = () => { 22 | const { counter: oldValue } = this.state; 23 | const counter = oldValue + 1; 24 | this.setState({ counter }); 25 | this.props.onAfterChange(counter); 26 | }; 27 | } 28 | 29 | const App = () => console.log(v)} />; 30 | 31 | const container = document.getElementById("root"); 32 | createRoot(container!).render( 33 | 34 | 35 | 36 | ); 37 | -------------------------------------------------------------------------------- /presentations/react-new-component/code/counter-functional.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC, useState } from "react"; 2 | import { createRoot } from "react-dom/client"; 3 | import "./index.css"; 4 | 5 | type BtnWithCounterProps = { 6 | onAfterChange: (v: number) => void; 7 | }; 8 | 9 | const BtnWithCounter: FC = ({ onAfterChange }) => { 10 | const [counter, setCounter] = useState(0); // setup state 11 | const handleClick = () => { // define handler 12 | const newValue = counter + 1; // implement logic 13 | setCounter(newValue); // update state 14 | onAfterChange(newValue); // invoke callback 15 | }; 16 | return ( 17 | 20 | ); 21 | }; 22 | 23 | const App = () => console.log(v)} />; 24 | 25 | const container = document.getElementById("root"); 26 | createRoot(container!).render( 27 | 28 | 29 | 30 | ); 31 | -------------------------------------------------------------------------------- /presentations/react-new-component/code/mb-class.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { PureComponent } from "react"; 3 | import { createRoot } from "react-dom/client"; 4 | 5 | type MessageBlockProps = { message: string }; 6 | 7 | class MessageBlock extends PureComponent { 8 | render() { 9 | const { message } = this.props; 10 | return
{message}
; 11 | } 12 | } 13 | 14 | const MyApp = () => ; 15 | 16 | const container = document.getElementById("root"); 17 | createRoot(container!).render( 18 | 19 | 20 | 21 | ); 22 | -------------------------------------------------------------------------------- /presentations/react-new-component/code/mb-functional.tsx: -------------------------------------------------------------------------------- 1 | import { FC } from "react"; 2 | import { createRoot } from "react-dom/client"; 3 | import React from "react"; 4 | 5 | type MessageBlockProps = { message: string }; 6 | 7 | const MessageBlock: FC = ({ message }) => ( 8 |
{message}
9 | ); 10 | 11 | const MyApp = () => ; 12 | 13 | const container = document.getElementById("root"); 14 | createRoot(container!).render( 15 | 16 | 17 | 18 | ); 19 | -------------------------------------------------------------------------------- /presentations/react-new-component/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | React For Beginners - Building React Component 9 | 10 | 11 | 12 | 13 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 |
34 |
35 |
36 |

37 | Free React Course
38 | For Beginners 39 |

40 |
41 | 42 |
43 |

L4 - Building React Component

44 |

45 | React For Beginners by Itera 46 |

47 |
48 | 49 |
50 |

Key Points

51 |
    52 |
  • What is component - quick recap
  • 53 |
  • Building "dumb" components - functional and class-based
  • 54 |
  • Building "smart" component - functional and class-based
  • 55 |
  • What and when to use
  • 56 |
  • Components - best practices
  • 57 |
58 |
59 | 60 | 61 | 62 | 63 |
64 |

Home task

65 |
    66 |
  • Add footer to your application
  • 67 |
  • Split the application you've built previously into components
  • 68 |
  • Create a component named `` which will render `a`. Component should:
  • 69 |
  • Be typed
  • 70 |
  • Accept custom text
  • 71 |
  • Accept custom url
  • 72 |
  • Accept callback that should be invoked before the click
  • 73 |
  • Put the new component into the footer
  • 74 |
  • When clicked, it should log into the console next text: `redirecting user to the next {link}` where `{link}` should be a's href
  • 75 |
76 |
77 | 78 |
79 |

Useful links

80 | 86 |
87 | 88 |
89 |

Join me

90 | 91 | 92 |
93 | 99 | Twitter 100 |
101 | 102 |
103 | 109 | GitHub 110 |
111 | 112 |
113 |
114 | 115 | 116 | 117 | 122 | 123 | 124 | -------------------------------------------------------------------------------- /presentations/react-router/code/react-routing.tsx: -------------------------------------------------------------------------------- 1 | import React, { Component, FC } from "react"; 2 | import { createRoot } from "react-dom/client"; 3 | import "./index.css"; 4 | 5 | import { 6 | BrowserRouter, 7 | Routes, 8 | Route, 9 | useParams, 10 | Navigate, 11 | Link, 12 | Outlet, 13 | } from "react-router-dom"; 14 | 15 | const UserPage = () => { 16 | const { userName } = useParams(); 17 | return ( 18 |
19 | Hello user: {userName} 20 |
21 | View: 22 |
23 |
24 | Default view 25 |
26 |
27 | Posts view 28 |
29 |
30 | ); 31 | }; 32 | 33 | type UserData = { user?: { authenticated: boolean } }; 34 | 35 | const UserPageGuard: FC = ({ user }) => 36 | user && user.authenticated ? : ; 37 | 38 | // Application 39 | class App extends Component { 40 | state = { authenticated: true }; 41 | render() { 42 | return ( 43 | 44 | 45 | 46 | } 49 | > 50 | 51 | 52 | 53 | 54 | 55 | ); 56 | } 57 | } 58 | 59 | const container = document.getElementById("root"); 60 | createRoot(container!).render( 61 | 62 | 63 | 64 | ); 65 | -------------------------------------------------------------------------------- /presentations/react-router/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | React For Beginners - Routing in React 9 | 10 | 11 | 12 | 13 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 |
34 |
35 |
36 |

37 | Free React Course
38 | For Beginners 39 |

40 |
41 | 42 |
43 |

L8 - Routing in React

44 |

45 | React For Beginners by Itera 46 |

47 |
48 | 49 |
50 |

Key Points

51 |
    52 |
  • A cup of theory
  • 53 |
  • Install react router
  • 54 |
  • Basic setup
  • 55 |
  • Data Binding
  • 56 |
  • Router guard
  • 57 |
58 |
59 | 60 | 61 | 62 |
63 |

Takeaway

64 |
    65 |
  • SPA - is a web application or website that interacts with the user by dynamically rewriting the current web page with new data
  • 66 |
  • In React we are using React Router to build SPA
  • 67 |
  • React router supports a lot of handy options - params, nested view, default routes
  • 68 |
69 |
70 | 71 |
72 |

Home task

73 |
    74 |
  • Add header for your application
  • 75 |
  • Create new page named `about`
  • 76 |
  • Move all content related yourself to the page about
  • 77 |
  • Add new query parameter named `ln` to your link like this: `https://8080?ln=ua`
  • 78 |
  • If `ln` equals `ua` all texts should be in Ukrainian language
  • 79 |
  • If `ln` equals `en` all text should be in English (feel free to use google translate if needed)
  • 80 |
81 |
82 | 83 |
84 |

Useful links

85 | 88 |
89 | 90 |
91 |

Join me

92 | 93 | 94 |
95 | 101 | Twitter 102 |
103 | 104 |
105 | 111 | GitHub 112 |
113 | 114 |
115 |
116 | 117 | 118 | 119 | 124 | 125 | 126 | -------------------------------------------------------------------------------- /presentations/react-state-management/code/index_ctx.tsx: -------------------------------------------------------------------------------- 1 | import React, { createContext, FC, useContext } from "react"; 2 | import { createRoot } from "react-dom/client"; 3 | import "./index.css"; 4 | 5 | // Context that acts as a store 6 | const Context = createContext({ 7 | nationality: "unknown", 8 | setNationality: (_: string) => {}, 9 | }); 10 | 11 | type UserInputProps = {}; 12 | 13 | // Component to show user details 14 | const L3: FC = () => { 15 | const { nationality } = useContext(Context); 16 | return {nationality}; 17 | }; 18 | 19 | // Some component #2 20 | const L2: FC = () => ; 21 | 22 | // Some component #1 23 | const L1: FC = () => ; 24 | 25 | // Simple styled button 26 | type BtnProps = { onClick: () => void; text: string }; 27 | const MyBtn: FC = ({ onClick, text }) => ( 28 | 31 | ); 32 | 33 | // pretend to be fetch 34 | const fetchUser = () => ({ nationality: "Ukrainian" }); 35 | 36 | // Some component with logic 37 | type SomeContainerProps = {}; 38 | const SomeContainer: FC = () => { 39 | const ctx = useContext(Context); 40 | const btnHandler = () => { 41 | const { nationality } = fetchUser(); 42 | ctx.setNationality(nationality); 43 | }; 44 | return ; 45 | }; 46 | 47 | // Application 48 | class App extends React.Component { 49 | state = { 50 | nationality: "Unknown", 51 | setNationality: (v: string) => { 52 | this.setState({ ...this.state, nationality: v }); 53 | }, 54 | }; 55 | render() { 56 | return ( 57 | <> 58 | 59 |

My App

60 | 61 | 62 |
63 | 64 | ); 65 | } 66 | } 67 | 68 | const container = document.getElementById("root"); 69 | createRoot(container!).render( 70 | 71 | 72 | 73 | ); 74 | -------------------------------------------------------------------------------- /presentations/react-state-management/code/index_general.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from "react"; 2 | import { createRoot } from "react-dom/client"; 3 | import "./index.css"; 4 | 5 | type UserInputProps = { nationality: string }; 6 | 7 | // Component to show user details 8 | const L3: FC = ({ nationality }) => ( 9 | {nationality} 10 | ); 11 | 12 | // Some component #2 13 | const L2: FC = ({ nationality }) => ( 14 | 15 | ); 16 | 17 | // Some component #1 18 | const L1: FC = ({ nationality }) => ( 19 | 20 | ); 21 | 22 | // Simple styled button 23 | type BtnProps = { onClick: () => void; text: string }; 24 | const MyBtn: FC = ({ onClick, text }) => ( 25 | 28 | ); 29 | 30 | // pretend to be fetch 31 | const fetchUser = () => ({ nationality: "Ukrainian" }); 32 | 33 | // Some component with logic 34 | type SomeContainerProps = { onDetailGet: (v: string) => void }; 35 | const SomeContainer: FC = ({ onDetailGet }) => { 36 | // Performance issue 37 | const btnHandler = () => { 38 | const userDetails = fetchUser(); 39 | onDetailGet(userDetails.nationality); 40 | }; 41 | return ; 42 | }; 43 | 44 | // Application 45 | class App extends React.Component { 46 | state = { nationality: "Unknown" }; 47 | render() { 48 | const { nationality } = this.state; 49 | return ( 50 | <> 51 |

My App

52 | 53 | 54 | 55 | ); 56 | } 57 | setNationality = (v: string) => this.setState({ nationality: v }); 58 | } 59 | 60 | const container = document.getElementById("root"); 61 | createRoot(container!).render( 62 | 63 | 64 | 65 | ); 66 | -------------------------------------------------------------------------------- /presentations/react-state-management/code/index_mobx.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from "react"; 2 | import { createRoot } from "react-dom/client"; 3 | import { makeAutoObservable } from "mobx"; 4 | import { observer } from "mobx-react-lite"; 5 | import "./index.css"; 6 | 7 | // store 8 | class UserDetailsStore { 9 | nationality = "unknown"; 10 | constructor() { 11 | // important! 12 | makeAutoObservable(this); 13 | } 14 | 15 | setNationality(v: string) { 16 | this.nationality = v; 17 | } 18 | } 19 | 20 | const userDetailsStore = new UserDetailsStore(); 21 | 22 | type UserInputProps = {}; 23 | 24 | // Component to show user details 25 | const L3: FC = observer(() => { 26 | const { nationality } = userDetailsStore; 27 | return {nationality}; 28 | }); 29 | 30 | // Some component #2 31 | const L2: FC = () => ; 32 | 33 | // Some component #1 34 | const L1: FC = () => ; 35 | 36 | // Simple styled button 37 | type BtnProps = { onClick: () => void; text: string }; 38 | const MyBtn: FC = ({ onClick, text }) => ( 39 | 42 | ); 43 | 44 | // pretend to be fetch 45 | const fetchUser = () => ({ nationality: "Ukrainian" }); 46 | 47 | // Some component with logic 48 | type SomeContainerProps = {}; 49 | const SomeContainer: FC = () => { 50 | const btnHandler = () => { 51 | const { nationality } = fetchUser(); 52 | userDetailsStore.setNationality(nationality); 53 | }; 54 | return ; 55 | }; 56 | 57 | // Application 58 | class App extends React.Component { 59 | render() { 60 | return ( 61 | <> 62 |

My App

63 | 64 | 65 | 66 | ); 67 | } 68 | } 69 | 70 | const container = document.getElementById("root"); 71 | createRoot(container!).render( 72 | 73 | 74 | 75 | ); 76 | -------------------------------------------------------------------------------- /presentations/react-state-management/code/index_rdx.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from "react"; 2 | import { createRoot } from "react-dom/client"; 3 | import { configureStore, createSlice } from "@reduxjs/toolkit"; 4 | import { Provider, useDispatch, useSelector } from "react-redux"; 5 | import "./index.css"; 6 | 7 | const userDetailsSlice = createSlice({ 8 | name: "userDetails", 9 | initialState: { 10 | nationality: "unknown", 11 | }, 12 | reducers: { 13 | setNationality: (state, action) => ({...state, nationality: action.payload}) 14 | }, 15 | }); 16 | 17 | const store = configureStore({ 18 | reducer: { 19 | userDetails: userDetailsSlice.reducer, 20 | }, 21 | }); 22 | 23 | type UserInputProps = {}; 24 | 25 | // Component to show user details 26 | const L3: FC = () => { 27 | const { nationality } = useSelector((state: any) => state.userDetails); 28 | return {nationality}; 29 | }; 30 | 31 | // Some component #2 32 | const L2: FC = () => ; 33 | 34 | // Some component #1 35 | const L1: FC = () => ; 36 | 37 | // Simple styled button 38 | type BtnProps = { onClick: () => void; text: string }; 39 | const MyBtn: FC = ({ onClick, text }) => ( 40 | 43 | ); 44 | 45 | // pretend to be fetch 46 | const fetchUser = () => ({ nationality: "Ukrainian" }); 47 | 48 | // Some component with logic 49 | type SomeContainerProps = {}; 50 | const SomeContainer: FC = () => { 51 | const dispatch = useDispatch(); 52 | const btnHandler = () => { 53 | const { nationality } = fetchUser(); 54 | dispatch(userDetailsSlice.actions.setNationality(nationality)); 55 | }; 56 | return ; 57 | }; 58 | 59 | // Application 60 | class App extends React.Component { 61 | render() { 62 | return ( 63 | <> 64 | 65 |

My App

66 | 67 | 68 |
69 | 70 | ); 71 | } 72 | } 73 | 74 | const container = document.getElementById("root"); 75 | createRoot(container!).render( 76 | 77 | 78 | 79 | ); 80 | -------------------------------------------------------------------------------- /presentations/react-state-management/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | React For Beginners - Managing State in React 9 | 10 | 11 | 12 | 13 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 |
34 |
35 |
36 |

37 | Free React Course
38 | For Beginners 39 |

40 |
41 | 42 |
43 |

L6 - Managing State in React

44 |

45 | React For Beginners by Itera 46 |

47 |
48 | 49 |
50 |

Key Points

51 |
    52 |
  • Problems with setState and useState
  • 53 |
  • Context
  • 54 |
  • Redux
  • 55 |
  • MobX
  • 56 |
57 |
58 | 59 | 60 | 61 | 62 |
63 |

Home task

64 |
    65 |
  • Using context write the timer that will have two buttons
  • 66 |
  • start and stop
  • 67 |
  • Timer should:
  • 68 |
  • Be stopped by default
  • 69 |
  • On start, App should display time in format HH:MM:SS
  • 70 |
  • On start page title should be changed to "Timer is running"
  • 71 |
  • On stop timer should be stopped, the latest value should be present
  • 72 |
  • Page title should be returned back to normal
  • 73 |
  • Install MobX
  • 74 |
  • Implement same functionality with MobX
  • 75 |
76 |
77 | 78 |
79 |

Useful links

80 | 87 |
88 | 89 |
90 |

Join me

91 | 92 | 93 |
94 | 100 | Twitter 101 |
102 | 103 |
104 | 110 | GitHub 111 |
112 | 113 |
114 |
115 | 116 | 117 | 118 | 123 | 124 | 125 | -------------------------------------------------------------------------------- /presentations/react-styling/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | React For Beginners - React - From CSS to CSSinJS 9 | 10 | 11 | 12 | 13 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 |
34 |
35 |
36 |

37 | Free React Course
38 | For Beginners 39 |

40 |
41 | 42 |
43 |

L5 - React - From CSS to CSSinJS

44 |

45 | React For Beginners by Itera 46 |

47 |
48 | 49 |
50 |

Key Points

51 |
    52 |
  • Default way
  • 53 |
  • Using CSS modules
  • 54 |
  • Preprocessors
  • 55 |
  • CSS in JS
  • 56 |
57 |
58 | 59 | 60 | 61 | 62 |
63 |

Home task

64 |
    65 |
  • Use CSS modules to style previously created application
  • 66 |
67 |
68 | 69 |
70 |

Useful links

71 | 78 |
79 | 80 |
81 |

Join me

82 | 83 | 84 |
85 | 91 | Twitter 92 |
93 | 94 |
95 | 101 | GitHub 102 |
103 | 104 |
105 |
106 | 107 | 108 | 109 | 114 | 115 | 116 | -------------------------------------------------------------------------------- /presentations/react-testing/imgs/failed-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Drag13/react-learning-course-short/748b4afdae58313d6cb12f2264c40d185db5aab5/presentations/react-testing/imgs/failed-example.png -------------------------------------------------------------------------------- /presentations/react-testing/imgs/pyramide.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Drag13/react-learning-course-short/748b4afdae58313d6cb12f2264c40d185db5aab5/presentations/react-testing/imgs/pyramide.png -------------------------------------------------------------------------------- /presentations/react-testing/imgs/snapshottesting.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Drag13/react-learning-course-short/748b4afdae58313d6cb12f2264c40d185db5aab5/presentations/react-testing/imgs/snapshottesting.png -------------------------------------------------------------------------------- /presentations/react-testing/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | React For Beginners - Testing Your Code 9 | 10 | 11 | 12 | 13 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 |
34 |
35 |
36 |

37 | Free React Course
38 | For Beginners 39 |

40 |
41 | 42 |
43 |

L11 - Testing Your Code

44 |

45 | React For Beginners by Itera 46 |

47 |
48 | 49 |
50 |

Key Points

51 |
    52 |
  • Why test
  • 53 |
  • Testing pyramid
  • 54 |
  • What should be tested
  • 55 |
  • Testing with Jest
  • 56 |
57 |
58 | 59 | 60 | 61 | 62 | 63 |
64 |

Useful links

65 | 71 |
72 | 73 |
74 |

Join me

75 | 76 | 77 |
78 | 84 | Twitter 85 |
86 | 87 |
88 | 94 | GitHub 95 |
96 | 97 |
98 |
99 | 100 | 101 | 102 | 107 | 108 | 109 | -------------------------------------------------------------------------------- /presentations/react-ui-lib/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | React For Beginners - UI Libraries 9 | 10 | 11 | 12 | 13 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 |
34 |
35 |
36 |

37 | Free React Course
38 | For Beginners 39 |

40 |
41 | 42 |
43 |

L10 - UI Libraries

44 |

45 | React For Beginners by Itera 46 |

47 |
48 | 49 |
50 |

Key Points

51 |
    52 |
  • Purpose
  • 53 |
  • Cons
  • 54 |
  • How to choose
  • 55 |
  • Material UI
  • 56 |
57 |
58 | 59 | 60 | 61 | 62 | 63 |
64 |

Useful links

65 | 71 |
72 | 73 |
74 |

Join me

75 | 76 | 77 |
78 | 84 | Twitter 85 |
86 | 87 |
88 | 94 | GitHub 95 |
96 | 97 |
98 |
99 | 100 | 101 | 102 | 107 | 108 | 109 | -------------------------------------------------------------------------------- /presentations/shared/helpers.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --footer-size: 2rem; 3 | } 4 | 5 | .left { 6 | text-align: left; 7 | } 8 | 9 | pre > code.pre-code-l { 10 | max-height: 500px; 11 | } 12 | 13 | .reveal .green { 14 | color: #17ff2e; 15 | } 16 | 17 | .reveal .red { 18 | color: red; 19 | } 20 | 21 | .scrollable-slide { 22 | height: 100%; 23 | overflow-y: auto !important; 24 | } 25 | 26 | ::-webkit-scrollbar { 27 | width: 6px; 28 | } 29 | ::-webkit-scrollbar-track { 30 | -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3); 31 | } 32 | ::-webkit-scrollbar-thumb { 33 | background-color: #333; 34 | } 35 | ::-webkit-scrollbar-corner { 36 | background-color: #333; 37 | } 38 | 39 | .footer { 40 | position: absolute; 41 | display: flex; 42 | bottom: 0; 43 | max-height: var(--footer-size, 32px); 44 | width: 100%; 45 | flex-direction: row-reverse; 46 | } 47 | 48 | .footer img { 49 | max-height: var(--footer-size, 32px); 50 | } 51 | 52 | .ukraine { 53 | color: #fff; 54 | line-height: 1em; 55 | overflow-wrap: anywhere; 56 | background: linear-gradient(to bottom, #015bbb 50%, #fed500 50%); 57 | padding: 1.5rem 0.5rem; 58 | } 59 | 60 | .two-column { 61 | display: flex; 62 | text-align: left; 63 | flex-direction: row; 64 | } 65 | -------------------------------------------------------------------------------- /presentations/shared/imgs/39900370_1138320566319759_9157901823137284096_n.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Drag13/react-learning-course-short/748b4afdae58313d6cb12f2264c40d185db5aab5/presentations/shared/imgs/39900370_1138320566319759_9157901823137284096_n.jpg -------------------------------------------------------------------------------- /presentations/shared/imgs/Itera-logo-white-fuchsia.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Drag13/react-learning-course-short/748b4afdae58313d6cb12f2264c40d185db5aab5/presentations/shared/imgs/Itera-logo-white-fuchsia.jpg -------------------------------------------------------------------------------- /presentations/shared/imgs/babich.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Drag13/react-learning-course-short/748b4afdae58313d6cb12f2264c40d185db5aab5/presentations/shared/imgs/babich.jpg -------------------------------------------------------------------------------- /presentations/shared/imgs/njsr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Drag13/react-learning-course-short/748b4afdae58313d6cb12f2264c40d185db5aab5/presentations/shared/imgs/njsr.png -------------------------------------------------------------------------------- /presentations/shared/imgs/title.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Drag13/react-learning-course-short/748b4afdae58313d6cb12f2264c40d185db5aab5/presentations/shared/imgs/title.png -------------------------------------------------------------------------------- /presentations/shared/index.css: -------------------------------------------------------------------------------- 1 | @import './vscode.css'; 2 | @import './helpers.css'; 3 | -------------------------------------------------------------------------------- /presentations/shared/init.js: -------------------------------------------------------------------------------- 1 | import Reveal from 'reveal.js'; 2 | import Markdown from 'reveal.js/plugin/markdown/markdown.esm.js'; 3 | import Highlight from 'reveal.js/plugin/highlight/highlight.esm.js'; 4 | 5 | (function init() { 6 | const reveal = new Reveal({ 7 | plugins: [Markdown, Highlight], 8 | hash: true, 9 | }); 10 | 11 | function resetSlideScrolling(slide) { 12 | slide.classList.remove('scrollable-slide'); 13 | } 14 | 15 | function handleSlideScrolling(slide) { 16 | if (slide.scrollHeight >= 800) { 17 | slide.classList.add('scrollable-slide'); 18 | } 19 | } 20 | 21 | reveal.addEventListener('ready', function (event) { 22 | handleSlideScrolling(event.currentSlide); 23 | }); 24 | 25 | reveal.addEventListener('slidechanged', function (event) { 26 | const isFirstSlide = event.indexh === 0 && event.indexv === 0; 27 | const display = isFirstSlide ? 'flex' : 'none'; 28 | const footer = document.querySelector('.footer'); 29 | footer && (footer.style.display = display); 30 | if (event.previousSlide) { 31 | resetSlideScrolling(event.previousSlide); 32 | } 33 | handleSlideScrolling(event.currentSlide); 34 | }); 35 | 36 | reveal.initialize(); 37 | })(); 38 | -------------------------------------------------------------------------------- /presentations/shared/vscode.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Visual Studio 2015 dark style 3 | * Author: Nicolas LLOBERA 4 | */ 5 | 6 | .hljs { 7 | background: #1E1E1E; 8 | color: #DCDCDC; 9 | } 10 | 11 | .hljs-keyword, 12 | .hljs-literal, 13 | .hljs-symbol, 14 | .hljs-name { 15 | color: #569CD6; 16 | } 17 | .hljs-link { 18 | color: #569CD6; 19 | text-decoration: underline; 20 | } 21 | 22 | .hljs-built_in, 23 | .hljs-type { 24 | color: #4EC9B0; 25 | } 26 | 27 | .hljs-number, 28 | .hljs-class { 29 | color: #B8D7A3; 30 | } 31 | 32 | .hljs-string, 33 | .hljs-meta .hljs-string { 34 | color: #D69D85; 35 | } 36 | 37 | .hljs-regexp, 38 | .hljs-template-tag { 39 | color: #9A5334; 40 | } 41 | 42 | .hljs-subst, 43 | .hljs-function, 44 | .hljs-title, 45 | .hljs-params, 46 | .hljs-formula { 47 | color: #DCDCDC; 48 | } 49 | 50 | .hljs-comment, 51 | .hljs-quote { 52 | color: #57A64A; 53 | font-style: italic; 54 | } 55 | 56 | .hljs-doctag { 57 | color: #608B4E; 58 | } 59 | 60 | .hljs-meta, 61 | .hljs-meta .hljs-keyword, 62 | 63 | .hljs-tag { 64 | color: #9B9B9B; 65 | } 66 | 67 | .hljs-variable, 68 | .hljs-template-variable { 69 | color: #BD63C5; 70 | } 71 | 72 | .hljs-attr, 73 | .hljs-attribute { 74 | color: #9CDCFE; 75 | } 76 | 77 | .hljs-section { 78 | color: gold; 79 | } 80 | 81 | .hljs-emphasis { 82 | font-style: italic; 83 | } 84 | 85 | .hljs-strong { 86 | font-weight: bold; 87 | } 88 | 89 | /*.hljs-code { 90 | font-family:'Monospace'; 91 | }*/ 92 | 93 | .hljs-bullet, 94 | .hljs-selector-tag, 95 | .hljs-selector-id, 96 | .hljs-selector-class, 97 | .hljs-selector-attr, 98 | .hljs-selector-pseudo { 99 | color: #D7BA7D; 100 | } 101 | 102 | .hljs-addition { 103 | background-color: #144212; 104 | display: inline-block; 105 | width: 100%; 106 | } 107 | 108 | .hljs-deletion { 109 | background-color: #600; 110 | display: inline-block; 111 | width: 100%; 112 | } -------------------------------------------------------------------------------- /presentations/solid/body.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

4 | SOLID можна запам'ятати за 1 один день.
А зрозуміти через 5 п'ять років.

5 | 6 | 7 |
8 |

Навіщо взагалі цей SOLID?

9 |
10 |
11 | 12 |
13 |
14 |

Принцип єдиної відповідальності

15 |
16 | 17 |
18 |

Кожен об'єкт має виконувати лише один обов'язок або мати єдину причину для зміни

19 |
20 | 21 |
22 | 23 |
24 | 25 |
26 |
export function User() {
 27 |   const [user, setUser] = useState<User | null>();
 28 | 
 29 |   useEffect(() => {
 30 |     getUser().then(setUser);
 31 |   }, []);
 32 | 
 33 |   if (!user) {
 34 |     return <div>Loading...</div>;
 35 |   }
 36 | 
 37 |   return <p>Hello, {user.fullName}</p>;
 38 | }
 39 | 
 40 | function getUser() {
 41 |   return fetch('https://jsonplaceholder.com/users/1')
 42 |     .then((response) => response.json())
 43 |     .then(({ firstName, secondName }) => ({
 44 |       fullName: `${firstName} ${secondName}`,
 45 |     }));
 46 | }
 47 |     
48 |
49 | 50 |
51 |

Головна ідея - зменшити крихкість коду та складність його окремих елементів

52 |
53 | 54 |
55 | Головна складність SRP - баланс між наочністю коду і трудовитратами з однієї сторони та 56 | крихкістю/складгістю не розбитого коду з іншої сторони 57 |
58 |
59 | 60 |
61 |
62 |

Принцип відкритої закритості

63 |
64 | 65 |
66 | Програмні сутності повинні бути відкритими для розширення, але закритими для змін. 67 |
68 | 69 |
Тобто, має бути спосіб змінювати поведінку коду без потреби зміни коду
70 | 71 |
72 | 73 |
74 | 75 |
76 |

Найпростіший приклад

77 |
fetch('https://test.org', { method: 'GET' });
 78 | // and /  /
 79 | fetch('https://test.org', { method: 'POST' });
 80 |     
81 |
82 | 83 |
84 |

Приклад з React

85 |
type MoneyProps = {
 86 |   value: number;
 87 |   currency: "₴" | "$" | "€";
 88 | };
 89 | 
 90 | export function Money({ value, currency }: MoneyProps) {
 91 |   const amount = value.toFixed(2);
 92 |   if (currency === "$") {
 93 |     return (
 94 |       <span className="currency">
 95 |         {currency} {amount}
 96 |       </span>
 97 |     );
 98 |   }
 99 | 
100 |   if (currency === "₴") {
101 |     return (
102 |       <span className="currency">
103 |         {amount} {currency}
104 |       </span>
105 |     );
106 |   }
107 | }
108 |     
109 |
110 | 111 |
112 | Головна ідея - розширення функціоналу з мінімальною зміною вже існуючого коду, що зменшує 113 | вірогідність "зламати" існуючий функціонал 114 |
115 | 116 |
117 |

118 | Головна складність - така гнучкість зменшує наочність коду, що погіршує його читабельність та 119 | може збільшити складність 120 |

121 |
122 |
123 | 124 |
125 |
126 |

Принцип підстановки Лісков

127 |
128 | 129 |
Об'єкти в програмі можуть бути заміненими їх нащадками без зміни коду програми.
130 | 131 |
132 | Простими словами - всі нащадки мають бути написані так, щоб їх можна було використовувати там, 133 | де використовується їх батьки 134 |
135 | 136 |
137 | 138 |
139 |

Простий приклад

140 |
class SomeComponent extends PureComponent {
141 |   render() {} /*😈*/
142 | }
143 |     
144 |
145 | 146 |
147 |

Трохи хитріше

148 | 149 |
class SomeComponent extends PureComponent {
150 |   setState = null; /*😈*/
151 |   render() {
152 |     return <>He-he-he</>;
153 |   }
154 | }
155 |     
156 |
157 | 158 |
159 | Проблема з LSP в тому, що він не завжди очевидний. Але, оскільки React підтримує композицію 160 | замість наслідування, це не становить великої проблеми 161 |
162 |
163 | 164 |
165 |
166 |

Принцип розділення інтерфейсу

167 |
168 | 169 |
Багато спеціалізованих інтерфейсів краще за один універсальний
170 | 171 |
172 | 173 |
174 | 175 |
176 |
interface IUser {
177 |   name: string;
178 |   login: () => Promise<void>;
179 | }
180 | 
181 | function UserGreeting({ user }: { user: IUser }) {
182 |   return <>Hello, {user.name}</>;
183 | }
184 | 
185 | function App() {
186 |   return <UserGreeting user={{ name: "Vitalii", login: () => Promise.resolve() }} />;
187 | }
188 |     
189 |
190 | 191 |
192 | Головна ідея - спрощення використання коду, оскільки споживач "бачить" лише то, що йому 193 | необхідно. Це також призводить до спрощення тестування, оскільки нам потрібно підміняти лише 194 | частину реалізації. 195 |
196 | 197 |
198 | Для JS/React цей принцип перетворюється на "не вимагай того, що не використовуєш" 199 |
200 | 201 |
202 |
// ✅ Use this 
203 | function UserGreeting({ userName }: { userName: string }) {
204 |   return <>Hello, {userName}</>;
205 | }
206 | 
207 | // ⛔ Not this
208 | function UserGreeting({ user }: { user: User }) {
209 |   return <>Hello, {user.userName}</>;
210 | }
211 |
212 |
213 | 214 |
215 |
216 |

Принцип інверсії залежностей

217 |
218 | 219 |
220 | Залежності всередині системи будуються на основі абстракцій, що не повинні залежати від деталей; 221 | навпаки, деталі мають залежати від абстракцій. 222 |
223 | 224 |
225 | Тобто, код не має покаладатися на конкретну реалізацію, а лише на інтерфейс або тип. 226 |
227 | 228 |
229 | 230 |
231 |
class UserApi {
232 |   getUser(id) {
233 |     return fetch(`https://.../user/${id}`);
234 |   }
235 | }
236 |
237 | 238 |
239 |
export function User() {
240 |   const [user, setUser] = useState<User | null>();
241 | 
242 |   useEffect(() => {
243 |     fetch('https://jsonplaceholder.com/users/1')
244 |       .then((response) => response.json())
245 |       .then(setUser);
246 |   }, []);
247 | 
248 |   if (!user) {
249 |     return <div>Loading...</div>;
250 |   }
251 | 
252 |   return <p>Hello, {user.fullName}</p>;
253 | }
254 |     
255 |
256 | 257 |
258 | Якщо ви використовуєте JS, достатньо не хардкодити конкретну реалізацію, а отримувати її ззовні. 259 |
260 |
261 | 262 |
263 |
264 | 265 |
266 | 267 |
268 | Головна складність SOLID загалом - не завчити визнчення принципів, а зрозуміти для чого вони 269 | існують і яку проблему вирішують 270 |
271 | 272 |
273 | А головна скоадність цієї лекції в тому, що ми намагаємося натягнути принципи OOP на 274 | фуннкціональну парадигму, яка панує в React 275 |
276 |
277 | -------------------------------------------------------------------------------- /presentations/solid/dip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Drag13/react-learning-course-short/748b4afdae58313d6cb12f2264c40d185db5aab5/presentations/solid/dip.png -------------------------------------------------------------------------------- /presentations/solid/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | React For Beginners - SOLID & React 9 | 10 | 11 | 12 | 13 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 |
34 |
35 |
36 |

37 | Free React Course
38 | For Beginners 39 |

40 |
41 | 42 |
43 |

L16 - SOLID & React

44 |

45 | React For Beginners by Itera 46 |

47 |
48 | 49 |
50 |

Key Points

51 |
    52 |
  • Single Responsibility Principle
  • 53 |
  • Open Closed Principle
  • 54 |
  • Liskov Substitution Principle
  • 55 |
  • Interface Segregation Principle
  • 56 |
  • Dependency inversion Principle
  • 57 |
58 |
59 | 60 | 61 | 62 |
63 |

Takeaway

64 |
    65 |
  • Keep your code simple
  • 66 |
  • Don't hardcode
  • 67 |
  • Don't change to the code unpredictable
  • 68 |
  • Ask only needed
  • 69 |
  • Don't hardcode (yea, again)
  • 70 |
71 |
72 | 73 | 74 | 75 |
76 |

Join me

77 | 78 | 79 |
80 | 86 | Twitter 87 |
88 | 89 |
90 | 96 | GitHub 97 |
98 | 99 |
100 |
101 | 102 | 103 | 104 | 109 | 110 | 111 | -------------------------------------------------------------------------------- /presentations/solid/isp.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Drag13/react-learning-course-short/748b4afdae58313d6cb12f2264c40d185db5aab5/presentations/solid/isp.jpg -------------------------------------------------------------------------------- /presentations/solid/iuser-big.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Drag13/react-learning-course-short/748b4afdae58313d6cb12f2264c40d185db5aab5/presentations/solid/iuser-big.png -------------------------------------------------------------------------------- /presentations/solid/lsp-false.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Drag13/react-learning-course-short/748b4afdae58313d6cb12f2264c40d185db5aab5/presentations/solid/lsp-false.png -------------------------------------------------------------------------------- /presentations/solid/ocp.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Drag13/react-learning-course-short/748b4afdae58313d6cb12f2264c40d185db5aab5/presentations/solid/ocp.jpg -------------------------------------------------------------------------------- /presentations/solid/owl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Drag13/react-learning-course-short/748b4afdae58313d6cb12f2264c40d185db5aab5/presentations/solid/owl.png -------------------------------------------------------------------------------- /presentations/solid/srp.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Drag13/react-learning-course-short/748b4afdae58313d6cb12f2264c40d185db5aab5/presentations/solid/srp.jpg -------------------------------------------------------------------------------- /presentations/typescript-intro/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | React For Beginners - TypeScript For Beginners 9 | 10 | 11 | 12 | 13 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 |
34 |
35 |
36 |

37 | Free React Course
38 | For Beginners 39 |

40 |
41 | 42 |
43 |

L12 - TypeScript For Beginners

44 |

45 | React For Beginners by Itera 46 |

47 |
48 | 49 |
50 |

Key Points

51 |
    52 |
  • Primitives
  • 53 |
  • Functions
  • 54 |
  • CustomTypes
  • 55 |
  • Generics
  • 56 |
  • TypeAssertions
  • 57 |
58 |
59 | 60 | 61 | 62 |
63 |

Takeaway

64 |
    65 |
  • TypeScript - almost the same JavaScript but with built time type checking
  • 66 |
  • In a nutshell, TypeScript is pretty easy
  • 67 |
68 |
69 | 70 | 71 |
72 |

Useful links

73 | 79 |
80 | 81 |
82 |

Join me

83 | 84 | 85 |
86 | 92 | Twitter 93 |
94 | 95 |
96 | 102 | GitHub 103 |
104 | 105 |
106 |
107 | 108 | 109 | 110 | 115 | 116 | 117 | -------------------------------------------------------------------------------- /presentations/web-tools/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | React For Beginners - Useful tools for the web developer 9 | 10 | 11 | 12 | 13 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 |
34 |
35 |
36 |

37 | Free React Course
38 | For Beginners 39 |

40 |
41 | 42 |
43 |

L13 - Useful tools for the web developer

44 |

45 | React For Beginners by Itera 46 |

47 |
48 | 49 |
50 |

Key Points

51 |
    52 |
  • EsLint
  • 53 |
  • Prettier
  • 54 |
  • Husky
  • 55 |
  • Code Snippets
  • 56 |
57 |
58 | 59 | 60 | 61 | 62 | 63 |
64 |

Useful links

65 | 72 |
73 | 74 |
75 |

Join me

76 | 77 | 78 |
79 | 85 | Twitter 86 |
87 | 88 |
89 | 95 | GitHub 96 |
97 | 98 |
99 |
100 | 101 | 102 | 103 | 108 | 109 | 110 | -------------------------------------------------------------------------------- /templates/_body.html: -------------------------------------------------------------------------------- 1 | {{#keyPoints}} 2 | 3 |
4 |
5 |

{{{.}}}

6 |
7 |
8 | {{/keyPoints}} 9 | -------------------------------------------------------------------------------- /templates/_full-program.md: -------------------------------------------------------------------------------- 1 | # {{title}} 2 | {{#lessons}} 3 | 4 | ## {{i}} {{type}}: {{title}} 5 | 6 | ### Key points: 7 | 8 | {{#keyPoints}} 9 | - {{{.}}} 10 | {{/keyPoints}} 11 | 12 | {{#youtube}} 13 | Talk: {{{.}}} 14 | {{/youtube}} 15 | 16 | {{#presentation}} 17 | Presentation - {{{.}}} 18 | 19 | {{/presentation}} 20 | {{#prerequisite.length}} 21 | ### Prerequisite 22 | 23 | {{/prerequisite.length}} 24 | {{#prerequisite}} 25 | - {{{.}}} 26 | {{/prerequisite}} 27 | {{#prerequisite.length}} 28 | 29 | {{/prerequisite.length}} 30 | ### Home task: 31 | 32 | {{^hometask}} 33 | Not specified 34 | {{/hometask}} 35 | {{#hometask}} 36 | - {{{.}}} 37 | {{/hometask}} 38 | {{/lessons}} -------------------------------------------------------------------------------- /templates/_layout.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | React For Beginners - {{title}} 9 | 10 | 11 | 12 | 13 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 |
34 |
35 |
36 |

37 | Free React Course
38 | For Beginners 39 |

40 |
41 | 42 |
43 |

L{{i}} - {{title}}

44 |

45 | React For Beginners by Itera 46 |

47 |
48 | 49 | {{#keyPoints.length}} 50 |
51 |

Key Points

52 |
    53 | {{#keyPoints}} 54 |
  • {{{.}}}
  • 55 | {{/keyPoints}} 56 |
57 |
58 | {{/keyPoints.length}} 59 | 60 | 61 | 62 | {{#takeaways.length}} 63 |
64 |

Takeaway

65 |
    66 | {{#takeaways}} 67 |
  • {{{.}}}
  • 68 | {{/takeaways}} 69 |
70 |
71 | {{/takeaways.length}} 72 | 73 | {{#hometask.length}} 74 |
75 |

Home task

76 |
    77 | {{#hometask}} 78 |
  • {{{.}}}
  • 79 | {{/hometask}} 80 |
81 |
82 | {{/hometask.length}} 83 | 84 | {{#links.length}} 85 |
86 |

Useful links

87 |
    88 | {{#links}} 89 |
  • {{0}}
  • 90 | {{/links}} 91 |
92 |
93 | {{/links.length}} 94 | 95 |
96 |

Join me

97 | 98 | 99 |
100 | 106 | Twitter 107 |
108 | 109 |
110 | 116 | GitHub 117 |
118 | 119 |
120 |
121 | 122 | 123 | 124 | 129 | 130 | 131 | -------------------------------------------------------------------------------- /templates/_readme.md: -------------------------------------------------------------------------------- 1 | [![StandsWithUkraine](https://raw.githubusercontent.com/Drag13/drag13.github.io/development/swu.PNG)](https://savelife.in.ua/en/donate/) 2 | 3 | # {{title}} 4 | 5 | ## [Watch on YouTube](https://www.youtube.com/channel/UCg-txtmOEQ8BniR8008O1mA) 6 | 7 | ## About 8 | 9 | **DISCLAIMER:** 10 | All requests to "remove politics" will be removed completely without any comments. If you have another opinion - just skip this course. 11 | 12 | This repo dedicated to the course "React for Beginners". The course was created to support Ukraine 🇺🇦 and Ukrainians in the war against russia. It's completely free and open-sourced. Feel free to contribute or make any relevant suggestions. 13 | 14 | - Full program is [here](PROGRAM.md) 15 | - Presentations can be found here - [https://drag13.io/react-learning-course-short/react-intro](https://drag13.io/react-learning-course-short/react-intro) where is the name of the lesson 16 | - Changelog is [here](CHANGELOG.md) 17 | - Video - published [here](https://www.youtube.com/channel/UCg-txtmOEQ8BniR8008O1mA) 18 | - Technical details are [here](devlog.md) 19 | 20 | ## Roadmap 21 | 22 | * Course program - done ✅ 23 | * Repository setup - done ✅ 24 | * Prepare materials - done ✅ 25 | * On-line - done ✅ 26 | * Retrospective - done ✅ 27 | * Updates - done ✅ 28 | * Second iteration - done ✅ 29 | 30 | ## PreRequisites 31 | 32 | Basic knowledge with HTML/CSS/JS 33 | 34 | Self check: 35 | 36 | - Example of the block element, how to draw a button 37 | - What is the width of block element, how to center element (vertically and horizontally) 38 | - What does `.map` returns, how to sum all values in array 39 | 40 | Basic knowledge with GIT: 41 | 42 | Self Check: 43 | 44 | - How to create new repository, how to push to remote 45 | 46 | Existing account at [https://github.com](https://github.com) 47 | Installed [Node.JS](https://nodejs.org/en/) with NPM 48 | Installed [VsCode](https://code.visualstudio.com/) 49 | 50 | ## Program summary 51 | 52 | {{#lessons}} 53 | ### {{i}}: {{#link}}[{{title}}]({{{.}}}){{/link}}{{^link}}{{title}}{{/link}} 54 | 55 | {{/lessons}} 56 | ## Donations 57 | 58 | All donations are highly welcomed. You can donate any amount to the [National Bank of Ukraine directly](https://bank.gov.ua/en/news/all/natsionalniy-bank-vidkriv-spetsrahunok-dlya-zboru-koshtiv-na-potrebi-armiyi) or to the well known [charity fund Come Back Alive](https://www.comebackalive.in.ua/donate). 59 | 60 | Feel free to contact me directly if any question 61 | 62 | ## Sponsors 63 | 64 | [![](/presentations/shared/imgs/Itera-logo-white-fuchsia.jpg)](itera.com) 65 | 66 | ## Information support 67 | 68 | [![beerjs](./presentations/shared/imgs/39900370_1138320566319759_9157901823137284096_n.jpg)](https://t.me/beerJSZhytomyr) 69 | 70 | [![node.recipes](./presentations/shared/imgs/njsr.png)](http://node.recipes/) 71 | 72 | [![Babich Lviv CSS](./presentations/shared/imgs/babich.jpg)](https://t.me/toisamyibabich) -------------------------------------------------------------------------------- /tools/docs.js: -------------------------------------------------------------------------------- 1 | const { render } = require('mustache'); 2 | const { readFile, writeFile } = require('./fs'); 3 | 4 | function renderProgram(data) { 5 | const template = readFile('./templates/_full-program.md'); 6 | const res = render(template, data); 7 | writeFile('PROGRAM.md', res); 8 | } 9 | 10 | function renderReadme(data) { 11 | const template = readFile('./templates/_readme.md'); 12 | const res = render(template, data); 13 | writeFile('README.md', res); 14 | } 15 | 16 | (function cli() { 17 | const data = require('../course.json'); 18 | const lessons = data.lessons.filter((x) => !x.hidden); 19 | data.lessons = lessons.map((l, i) => { 20 | const presentation = l.published 21 | ? `https://drag13.io/react-learning-course-short/${l.name}` 22 | : ''; 23 | const link = l.youtube ?? presentation; 24 | 25 | return { ...l, i, presentation, link }; 26 | }); 27 | renderProgram(data); 28 | renderReadme(data); 29 | })(process.argv.splice(2)); 30 | -------------------------------------------------------------------------------- /tools/encode.js: -------------------------------------------------------------------------------- 1 | const { readFile, writeFile } = require("./fs"); 2 | const { join } = require("path"); 3 | 4 | (function name(params) { 5 | const filePath = params[0] ?? `../sample.tsx`; 6 | const fullPath = join(__dirname, filePath); 7 | const file = readFile(fullPath); 8 | const text = file.replaceAll("<", "<").replaceAll(">", ">"); 9 | const result = `
${text}
`; 10 | writeFile("test.txt", result); 11 | })(process.argv.splice(2)); 12 | -------------------------------------------------------------------------------- /tools/fs.js: -------------------------------------------------------------------------------- 1 | const { readFileSync, writeFileSync, existsSync } = require('fs'); 2 | 3 | const writeFile = (path, data) => writeFileSync(path, data, { encoding: 'utf-8' }); 4 | 5 | const readFile = (path) => readFileSync(path, { encoding: 'utf-8' }); 6 | 7 | const exists = (path) => existsSync(path); 8 | 9 | const ensureExists = (path) => { 10 | if (exists(path)) { 11 | return; 12 | } 13 | throw new Error(`File: "${path}" not exists`); 14 | }; 15 | 16 | module.exports = { 17 | writeFile, 18 | readFile, 19 | exists, 20 | ensureExists, 21 | }; 22 | -------------------------------------------------------------------------------- /tools/game.mjs: -------------------------------------------------------------------------------- 1 | import { loadCSV, save, smartMask, splitBySum } from './game.tools.mjs'; 2 | 3 | const mapToUser = (input) => { 4 | return { 5 | sum: +input.Sum, 6 | name: input.Name, 7 | }; 8 | }; 9 | 10 | (async function () { 11 | const data = await loadCSV('./input.csv', mapToUser, { 12 | separator: ';', 13 | filter: ({ name }) => !!name, 14 | }); 15 | const masked = data.map((x) => ({ ...x, name: smartMask(3, x.name) })); 16 | const list = splitBySum(50, 'sum', masked); 17 | save('res.txt', list); 18 | })(); 19 | -------------------------------------------------------------------------------- /tools/game.tools.mjs: -------------------------------------------------------------------------------- 1 | import { createReadStream, existsSync, writeFileSync } from 'fs'; 2 | import csv from 'csv-parser'; 3 | 4 | const writeFile = (path, data) => writeFileSync(path, data, { encoding: 'utf-8' }); 5 | 6 | const exists = (path) => existsSync(path); 7 | 8 | const ensureExists = (path) => { 9 | if (exists(path)) { 10 | return; 11 | } 12 | throw new Error(`File: "${path}" not exists`); 13 | }; 14 | 15 | /** 16 | * @param {string} fileName 17 | * @param {()=> T} mapper 18 | * @param {{separator?:string, filter: (v:T)=> boolean}} param2 19 | * @returns {Promise} 20 | */ 21 | export function loadCSV(fileName, mapper, { separator = ',', filter } = {}) { 22 | ensureExists(fileName); 23 | 24 | const resultPromise = new Promise((res) => { 25 | const results = []; 26 | createReadStream(fileName) 27 | .pipe(csv({ separator })) 28 | .on('data', (data) => results.push(mapper(data))) 29 | .on('end', () => { 30 | const finalList = typeof filter === 'function' ? results.filter(filter) : results; 31 | res(finalList); 32 | }); 33 | }); 34 | return resultPromise; 35 | } 36 | 37 | /** 38 | * 39 | * @param {number} maxSymbols 40 | * @param {string} input 41 | * @returns {string} 42 | */ 43 | export function maskString(maxSymbols, input) { 44 | const { length } = input; 45 | return maxSymbols > length 46 | ? `${input.slice(0, length)}****` 47 | : `${input.slice(0, maxSymbols)}****`; 48 | } 49 | 50 | /** 51 | * 52 | * @param {number} maxSymbols 53 | * @param {string} input 54 | * @param {number} boundary 55 | */ 56 | export function smartMask(maxSymbols, input) { 57 | const fio = input.split(/\s+/); 58 | const hasNameAndSecondName = fio.length > 1; 59 | if (hasNameAndSecondName) { 60 | return `${fio[0]} ${maskString(maxSymbols, fio[1])}`; 61 | } 62 | 63 | return maskString(maxSymbols, fio[0]); 64 | } 65 | 66 | export function splitBySum(sum = 50, fieldName = 'sum', arr) { 67 | return arr 68 | .map((x) => { 69 | const n = Math.floor(x[fieldName] / sum); 70 | return new Array(n).fill(x); 71 | }) 72 | .flat(); 73 | } 74 | 75 | export function save(path, data) { 76 | const text = data.map((x) => `${x.name}:${x.sum}`).join('\n'); 77 | writeFile(path, text); 78 | } 79 | 80 | export function normalizeFirstSymbol(fileName) { 81 | const file = readFileSync(fileName, { encoding: 'utf-8' }); 82 | const fixed = file.substring(1); 83 | writeFileSync(fileName, fixed); 84 | } 85 | -------------------------------------------------------------------------------- /tools/mask.js: -------------------------------------------------------------------------------- 1 | const { readFile, writeFile, ensureExists } = require('./fs'); 2 | const { join } = require('path'); 3 | const { cwd } = require('process'); 4 | 5 | (function main(params) { 6 | function simplePipe(arg, ...args) { 7 | return args.reduce((res, f) => f(res), arg); 8 | } 9 | 10 | function extractName(line, delimiter = ',') { 11 | return line.split(delimiter)[1]; 12 | } 13 | 14 | function extractTg(line) { 15 | return line; 16 | } 17 | 18 | function normalizeName(name) { 19 | return name.replace(/"/g, '').toLowerCase(); 20 | } 21 | 22 | function mask(name) { 23 | return `***${name.substring(2)}`; 24 | } 25 | 26 | function extractNamesFromGoogleForm(fileWithNames) { 27 | return fileWithNames 28 | .split('\n') 29 | .slice(1) 30 | .map((n) => simplePipe(n, extractName, normalizeName, mask)); 31 | } 32 | 33 | function extractNamesPlainList(fileWithNames) { 34 | return fileWithNames.map((n) => simplePipe(n, extractTg, normalizeName, mask)); 35 | } 36 | 37 | const pathToFileWithNames = join(cwd(), params[0] ?? `sample.csv`); 38 | const pathToOutputFile = join(cwd(), params[1] ?? `result.txt`); 39 | 40 | ensureExists(pathToFileWithNames); 41 | ensureExists(pathToOutputFile); 42 | 43 | const fileWithNames = readFile(pathToFileWithNames); 44 | const isGoogleFormsCSV = pathToFileWithNames.endsWith('.csv'); 45 | 46 | const names = ( 47 | isGoogleFormsCSV 48 | ? extractNamesFromGoogleForm(fileWithNames) 49 | : extractNamesPlainList(fileWithNames) 50 | ).join('\n'); 51 | 52 | writeFile(pathToOutputFile, names); 53 | })(process.argv.splice(2)); 54 | -------------------------------------------------------------------------------- /tools/scaffold.js: -------------------------------------------------------------------------------- 1 | const { render } = require("mustache"); 2 | const { join } = require("path"); 3 | const { readFile, writeFile, exists } = require("./fs"); 4 | const { cwd } = require("process"); 5 | const { mkdirSync } = require("fs"); 6 | const course = require("../course.json"); 7 | 8 | const ROOT = "./presentations"; 9 | const LAYOUT = "./templates/_layout.html"; 10 | const BODY = "./templates/_body.html"; 11 | 12 | function scaffold(layout, body, lesson, i) { 13 | const { name, title } = lesson; 14 | if (!name) { 15 | console.error( 16 | `\r\nLesson with title ${title} has no name. Scaffolding canceled` 17 | ); 18 | return; 19 | } 20 | 21 | const folder = join(cwd(), ROOT, name); 22 | const pathToBody = join(folder, "body.html"); 23 | const pathToLayout = join(folder, "index.html"); 24 | 25 | lesson.i = i; 26 | 27 | const folderExists = exists(folder); 28 | const bodyExists = folderExists && exists(pathToBody); 29 | 30 | if (!folderExists) { 31 | mkdirSync(folder); 32 | } 33 | 34 | // always regenerate index.html for consistency 35 | const indexHtml = render(layout, lesson); 36 | writeFile(pathToLayout, indexHtml); 37 | // don't regenerate body if it already in place 38 | if (!bodyExists) { 39 | const bodyHtml = render(body, lesson); 40 | writeFile(pathToBody, bodyHtml); 41 | } 42 | } 43 | 44 | (function cli([lessonName]) { 45 | const layout = readFile(LAYOUT); 46 | const body = readFile(BODY); 47 | 48 | course.lessons = course.lessons.filter(x=>!x.hidden); 49 | 50 | if (lessonName == null) { 51 | process.stdout.write(`Syncing ALL lectures...`); 52 | course.lessons.forEach((lesson, index) => 53 | scaffold(layout, body, lesson, index) 54 | ); 55 | 56 | console.log("DONE"); 57 | 58 | return; 59 | } 60 | 61 | const lessonNumber = course.lessons.findIndex((x) => x.name === lessonName); 62 | const lesson = course.lessons[lessonNumber]; 63 | 64 | if (!lesson) { 65 | console.log(`Lesson ${lessonName} doesn't exist, ABORTING...`); 66 | return; 67 | } 68 | 69 | process.stdout.write(`Scaffolding lesson ${lesson.name}... `); 70 | scaffold(layout, body, lesson, lessonNumber); 71 | 72 | console.log("DONE"); 73 | })(process.argv.splice(2)); 74 | -------------------------------------------------------------------------------- /tools/start.js: -------------------------------------------------------------------------------- 1 | const { exec } = require('child_process'); 2 | const { join } = require('path'); 3 | const { cwd } = require('process'); 4 | const course = require('../course.json'); 5 | const PORT = process.env.PORT ?? '1234'; 6 | 7 | (function cli(params) { 8 | const [lessonNumber] = params; 9 | 10 | if (lessonNumber == undefined) { 11 | console.log('\x1b[33m%s\x1b[0m', 'Please, specify lesson name to start first'); 12 | return logAvailableLessonNames(course.lessons); 13 | } 14 | 15 | const lesson = 16 | course.lessons[+lessonNumber] ?? course.lessons.find((x) => x.name === lessonNumber); 17 | 18 | if (!lesson) { 19 | console.log('\x1b[33m%s\x1b[0m', `Lesson "${lessonNumber}" doesn't exist, aborting`); 20 | return logAvailableLessonNames(course.lessons); 21 | } 22 | 23 | const sub = lesson.name; 24 | 25 | console.log(`Starting ${sub} presentation`); 26 | 27 | const path = join(cwd(), 'presentations', sub, 'index.html'); 28 | console.log(`Starting developer server on http://localhost:${PORT}`); 29 | exec(`npx parcel ${path}`); 30 | 31 | /** 32 | * Logs all lesson names available to start 33 | * @param {[{name:string}]} lessons 34 | */ 35 | function logAvailableLessonNames(lessons) { 36 | console.log('Available lessons are: '); 37 | lessons.map((l) => l.name).forEach((name) => console.log(name)); 38 | } 39 | })(process.argv.splice(2)); 40 | --------------------------------------------------------------------------------