├── .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 | [](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 | [](itera.com)
95 |
96 | ## Information support
97 |
98 | [](https://t.me/beerJSZhytomyr)
99 |
100 | [](http://node.recipes/)
101 |
102 | [](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 |
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 |
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 |
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 |
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 |
81 | Drag13 Twitter profile
82 |
84 |
85 |
86 |
Twitter
87 |
88 |
89 |
90 |
91 | Drag13 GitHub profile
92 |
94 |
95 |
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 |
97 | Drag13 Twitter profile
98 |
100 |
101 |
102 |
Twitter
103 |
104 |
105 |
106 |
107 | Drag13 GitHub profile
108 |
110 |
111 |
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 |
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 |
89 |
90 |
91 |
92 | index.tsx
93 |
94 |
95 |
96 |
97 |
98 | App.tsx
99 |
100 |
101 |
102 |
103 |
104 | App.test.tsx
105 |
106 |
107 |
108 |
109 | package.json
110 |
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 |
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 |
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 |
102 | Drag13 Twitter profile
103 |
105 |
106 |
107 |
Twitter
108 |
109 |
110 |
111 |
112 | Drag13 GitHub profile
113 |
115 |
116 |
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 |
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 |
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 |
77 | Drag13 Twitter profile
78 |
80 |
81 |
82 |
Twitter
83 |
84 |
85 |
86 |
87 | Drag13 GitHub profile
88 |
90 |
91 |
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 |
85 | Increase salary for 50
86 |
87 |
88 | Fire employee
89 |
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 |
88 | Drag13 Twitter profile
89 |
91 |
92 |
93 |
Twitter
94 |
95 |
96 |
97 |
98 | Drag13 GitHub profile
99 |
101 |
102 |
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 |
88 | Drag13 Twitter profile
89 |
91 |
92 |
93 |
Twitter
94 |
95 |
96 |
97 |
98 | Drag13 GitHub profile
99 |
101 |
102 |
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 |
86 | Drag13 Twitter profile
87 |
89 |
90 |
91 |
Twitter
92 |
93 |
94 |
95 |
96 | Drag13 GitHub profile
97 |
99 |
100 |
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 |
16 | Clicked {counter} times
17 |
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 |
18 | Clicked {counter} times
19 |
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 |
94 | Drag13 Twitter profile
95 |
97 |
98 |
99 |
Twitter
100 |
101 |
102 |
103 |
104 | Drag13 GitHub profile
105 |
107 |
108 |
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 |
96 | Drag13 Twitter profile
97 |
99 |
100 |
101 |
Twitter
102 |
103 |
104 |
105 |
106 | Drag13 GitHub profile
107 |
109 |
110 |
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 |
29 | {text}
30 |
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 |
26 | {text}
27 |
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 |
40 | {text}
41 |
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 |
41 | {text}
42 |
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 |
95 | Drag13 Twitter profile
96 |
98 |
99 |
100 |
Twitter
101 |
102 |
103 |
104 |
105 | Drag13 GitHub profile
106 |
108 |
109 |
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 |
86 | Drag13 Twitter profile
87 |
89 |
90 |
91 |
Twitter
92 |
93 |
94 |
95 |
96 | Drag13 GitHub profile
97 |
99 |
100 |
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 |
79 | Drag13 Twitter profile
80 |
82 |
83 |
84 |
Twitter
85 |
86 |
87 |
88 |
89 | Drag13 GitHub profile
90 |
92 |
93 |
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 |
79 | Drag13 Twitter profile
80 |
82 |
83 |
84 |
Twitter
85 |
86 |
87 |
88 |
89 | Drag13 GitHub profile
90 |
92 |
93 |
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 |
81 | Drag13 Twitter profile
82 |
84 |
85 |
86 |
Twitter
87 |
88 |
89 |
90 |
91 | Drag13 GitHub profile
92 |
94 |
95 |
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 |
87 | Drag13 Twitter profile
88 |
90 |
91 |
92 |
Twitter
93 |
94 |
95 |
96 |
97 | Drag13 GitHub profile
98 |
100 |
101 |
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 |
80 | Drag13 Twitter profile
81 |
83 |
84 |
85 |
Twitter
86 |
87 |
88 |
89 |
90 | Drag13 GitHub profile
91 |
93 |
94 |
95 |
GitHub
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
108 |
109 |
110 |
--------------------------------------------------------------------------------
/templates/_body.html:
--------------------------------------------------------------------------------
1 | {{#keyPoints}}
2 |
3 |
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 |
101 | Drag13 Twitter profile
102 |
104 |
105 |
106 |
Twitter
107 |
108 |
109 |
110 |
111 | Drag13 GitHub profile
112 |
114 |
115 |
116 |
GitHub
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
129 |
130 |
131 |
--------------------------------------------------------------------------------
/templates/_readme.md:
--------------------------------------------------------------------------------
1 | [](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 | [](itera.com)
65 |
66 | ## Information support
67 |
68 | [](https://t.me/beerJSZhytomyr)
69 |
70 | [](http://node.recipes/)
71 |
72 | [](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 |
--------------------------------------------------------------------------------