├── lessons ├── L03_managing-state │ ├── index.js │ ├── index.html │ ├── solution.js │ └── L03_managing-state.md ├── L02_mental-model │ ├── index.html │ ├── homework.js │ ├── index.js │ └── L02_mental-model.md ├── L04_separation │ ├── index.html │ ├── L04_separation.md │ ├── solution.js │ ├── index.js │ └── solution2.js ├── L00_getting-started │ └── L00_getting-started.md └── L01_introduction │ └── L01_introduction.md ├── README.md └── lib ├── normalize.css └── react.development.js /lessons/L03_managing-state/index.js: -------------------------------------------------------------------------------- 1 | function App() { 2 | return "Fix me!"; 3 | } 4 | 5 | ReactDOM.render( 6 | 7 | 8 | , 9 | document.getElementById("root") 10 | ); 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # เรียนรู้ React ฉบับ Foundation 2 | 3 | คอร์สสอน React เป็นภาษาไทย เน้นความเข้าใจและการฝึกฝน 4 | 5 | - 📹 [วิดีโออัพลง Youtube](https://www.youtube.com/playlist?list=PLLPNfc7CgMywiG-R6Jix_w8zqF_fxZFxr) ทั้งหมด 6 | - 📖 เนื้อหาและแบบฝึกหัดอยู่ใน repository นี้ 7 | - [Lesson 0 - Getting Started](/lessons/L00_getting-started/L00_getting-started.md) 8 | - [Lesson 1 - Introduction](/lessons/L01_introduction/L01_introduction.md) 9 | - [Lesson 2 - Mental Model](/lessons/L02_mental-model/L02_mental-model.md) 10 | - [Lesson 3 - Managing State](/lessons/L03_managing-state/L03_managing-state.md) 11 | - [Lesson 4 - Separation](/lessons/L04_separation/L04_separation.md) 12 | 13 | ``` 14 | *** ห้ามนำไปใช้ในเชิงพาณิชย์เด็ดขาด *** 15 | ``` 16 | -------------------------------------------------------------------------------- /lessons/L03_managing-state/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | React in Thai - Learn react foundation 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /lessons/L02_mental-model/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | React in Thai - Learn react foundation 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /lessons/L04_separation/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | React in Thai - Learn react foundation 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /lessons/L02_mental-model/homework.js: -------------------------------------------------------------------------------- 1 | /** 2 | * ใช้ snapshot mental model ทำความเข้าใจโดยไม่เปิด browser 3 | * เมื่อกดปุ่มจะเกิดอะไรขึ้นบ้าง?? 4 | * 5 | * เช็คความเข้าใจใน browser และเพิ่ม console.log ในจุดที่สนใจ 6 | */ 7 | function Homework_1() { 8 | const [loading, setLoading] = React.useState(false); 9 | const [count, setCount] = React.useState(0); 10 | return ( 11 |
12 |

{count}

13 | 24 |
25 | ); 26 | } 27 | 28 | /** 29 | * สร้าง media player แบบง่ายๆ 30 | * มีปุ่มย้อนกลับ (เล่นเพลงก่อนหน้า) 31 | * มีปุ่มเล่น/หยุด (เล่นเพลงก่อนหน้า) 32 | * มีปุ่มไปข้างหน้า (เล่นเพลงถัดไป) 33 | * 34 | * ให้เพิ่ม state และ event handler (onClick) ตามที่สมควร 35 | */ 36 | function Homework_2() { 37 | const SONGS = ["Lavender", "พิง", "เอาปากกามาวง"]; 38 | const pause = true; 39 | const currentSong = SONGS[0]; 40 | return ( 41 |
42 |

{currentSong}

43 |

{pause ? "หยุด" : "กำลังเล่น"}

44 | 45 | 46 | 47 |
48 | ); 49 | } 50 | 51 | function App() { 52 | const sectionLayoutStyle = { 53 | height: "100vh", 54 | display: "flex", 55 | flexDirection: "column", 56 | justifyContent: "center", 57 | alignItems: "center", 58 | }; 59 | return ( 60 |
61 |
62 | 63 |
64 |
65 | 66 |
67 |
68 | ); 69 | } 70 | 71 | ReactDOM.render( 72 | 73 | 74 | , 75 | document.getElementById("root") 76 | ); 77 | -------------------------------------------------------------------------------- /lessons/L02_mental-model/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * ### ตัวอย่างที่ 1 - counter 3 | */ 4 | function Counter() { 5 | const [count, setCount] = React.useState(0); 6 | return ( 7 | 8 | {count} 9 | 12 | 13 | ); 14 | } 15 | 16 | /** 17 | * ### ตัวอย่างที่ 2 - Search 18 | */ 19 | const MEMBERS = [ 20 | { name: "Tony Stark", level: 20, alive: false }, 21 | { name: "Captain", level: 99, alive: true }, 22 | { name: "Ant man", level: 38, alive: true }, 23 | ]; 24 | 25 | function Search() { 26 | // TODO: ควรจะมี state อะไรเก็บไว้บ้าง หากต้องการให้ค้นหาชื่อได้ 27 | return ( 28 | 29 | 30 | {MEMBERS.map((member) => ( 31 |
32 |
33 | {member.name} 34 |
35 |
36 | {member.level} 37 |
38 |
39 | {member.alive} 40 |
41 |
42 | ))} 43 |
44 | ); 45 | } 46 | 47 | /** 48 | * ### ตัวอย่างที่ 3 - magic counter 49 | */ 50 | function MagicCounter() { 51 | const [count, setCount] = React.useState(0); 52 | return ( 53 | 54 | {count} 55 | 65 | 66 | ); 67 | } 68 | 69 | function App() { 70 | const sectionLayoutStyle = { 71 | height: "100vh", 72 | display: "flex", 73 | flexDirection: "column", 74 | justifyContent: "center", 75 | alignItems: "center", 76 | }; 77 | return ( 78 |
79 |
80 | 81 |
82 |
83 | 84 |
85 |
86 | 87 |
88 |
89 | ); 90 | } 91 | 92 | ReactDOM.render( 93 | 94 | 95 | , 96 | document.getElementById("root") 97 | ); 98 | -------------------------------------------------------------------------------- /lessons/L04_separation/L04_separation.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | ◀ Previous Lesson 4 | 5 |  |  6 | 7 | Next Lesson ▶ (coming soon) 8 | 9 |
10 |
11 | 12 | >>> ไปหน้าสารบัญ <<< 13 | 14 |
15 | 16 | # บทที่ 4 - Separation 17 | 18 | > Video ประกอบการสอน: [https://www.youtube.com/watch?v=8qZvs8T59XQ](https://www.youtube.com/watch?v=8qZvs8T59XQ&list=PLLPNfc7CgMywiG-R6Jix_w8zqF_fxZFxr) 19 | 20 | จากบทที่แล้วเราได้สร้าง note application ที่มีฟีเจอร์พอสมควรแล้ว แต่ก่อนที่เราจะเพิ่มฟีเจอร์เข้าไปอีก เรามาลอง refactor แอพของเราให้แบ่งการทำงานออกเป็นส่วนย่อยๆก่อนดีกว่า 21 | 22 | การแบ่งแต่ละส่วนในแอพให้แยกจากกัน จะทำให้เราสามารถเทสง่ายขึ้น(คอร์สนี้ผมไม่ได้สอนการเขียนเทส) และเมื่อเราเพิ่มฟีเจอร์อื่นๆเข้าไปจะช่วยให้เราไม่สร้างผลกระทบต่อส่วนอื่นๆ แต่แน่นอนว่าในหลายๆครั้งการทำแบบนี้ก็ไม่ใช้เรื่องที่ดีเสมอไป ขึ้นอยู่กับความเหมาะสมในแต่ละสถานการณ์ 23 | 24 | ## แบ่งการทำงาน 25 | 26 | วิธีที่ง่ายที่สุดคือการแยกการแสดงผลกับ logic ออกจากกัน ในเคสของ note application นี้ผมจะแบ่งออกเป็น 2 components ย่อย โดยที่มี state ของ `notes` และ `selectedNote` อยู่ที่ App 27 | 28 | ![image](https://user-images.githubusercontent.com/18292247/147404904-67d2b151-ed23-4cff-ba0a-51e56bee04ee.png) 29 | 30 | - `NoteList`: แสดงผลโน้ตที่เรามี และมี interface ในการเลือกและลบโน้ต 31 | - `NoteEditor`: แสดงผลฟอร์ม 32 | 33 | ### NoteList 34 | 35 | Component นี้ควรทำหน้าที่ในการแสดงผลอยากเดียว ไม่ควรมี logic อยู่ภายในตัวเอง. ผลลัพธ์จะออกมาหน้าตาเป็นแบบนี้ 36 | 37 | ```js 38 | {}} 41 | onDeleteNote={(note) => {}} 42 | /> 43 | ``` 44 | 45 | ### NoteEditor 46 | 47 | สามารถรับ note เพื่อนำค่าต่างๆมาแสดงผลลงในฟอร์มได้ และ users สามารถแก้ไข บันทึก รวมถึงมีฟีเจอร์ undo, redo ภายในตัวเอง 48 | 49 | ```js 50 | {}} 53 | /> 54 | ``` 55 | 56 | 📹 สำหรับการ refactor ดูใน[วิดีโอ](https://www.youtube.com/watch?v=8qZvs8T59XQ&list=PLLPNfc7CgMywiG-R6Jix_w8zqF_fxZFxr)จะได้อรรถรสมากกว่า 😂 57 | 58 | --- 59 | 60 |
61 | 62 | ◀ Previous Lesson 63 | 64 |  |  65 | 66 | Next Lesson ▶ (coming soon) 67 | 68 |
69 |
70 | 71 | >>> ไปหน้าสารบัญ <<< 72 | 73 |
74 | -------------------------------------------------------------------------------- /lessons/L00_getting-started/L00_getting-started.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | ◀ Previous Lesson 4 | 5 |  |  6 | 7 | Next Lesson ▶ 8 | 9 |
10 |
11 | 12 | >>> ไปหน้าสารบัญ <<< 13 | 14 |
15 | 16 | # บทที่ 0 - เตรียมความพร้อม 17 | 18 | > Video ประกอบการสอน: [https://www.youtube.com/watch?v=fI06CT84FRQ](https://www.youtube.com/watch?v=fI06CT84FRQ&list=PLLPNfc7CgMywiG-R6Jix_w8zqF_fxZFxr) 19 | 20 | ## เนื้อหาในคอร์ส 21 | 22 | เนื้อหาในคอร์สนี้จะเน้นไปที่การสร้างรากฐานและความเข้าใจเกี่ยวกับ React เพื่อให้สามารถนำไปต่อยอด, ประยุกต์, และสร้างเว็บแอพลิเคชั่นที่มีความหลากหลายและซับซ้อนได้ 23 | 24 | สื่งที่ไม่ได้เน้นคือ 25 | - HTML, CSS 26 | - performance optimization 27 | - testing 28 | 29 | ## ระดับและความรู้พื้นฐาน 30 | 31 | จริงๆไม่จำเป็นมาก แต่ถ้ามีก็ดี ในวิดีโอจะพยายามแปะลิ้งค์ไว้ให้อ่านเพิ่มเติม 32 | 33 | - [basic HTML](https://developer.mozilla.org/en-US/docs/Learn/HTML) 34 | - [basic CSS](https://developer.mozilla.org/en-US/docs/Learn/CSS) 35 | - [basic JS](https://developer.mozilla.org/en-US/docs/Learn/Getting_started_with_the_web/JavaScript_basics) 36 | - [basic React](https://reactjs.org/docs/getting-started.html) (optional) 37 | 38 | > ในคอร์สจะไม่อธิบายรายละเอียดการใช้ HTML และ CSS (อาจมีพูดถึงบ้างเล็กน้อย) 39 | 40 | ## วิธีการเรียนการสอน 41 | 42 | - 📹 วิดีโอทั้งหมดจะอัดลงใน Youtube 43 | - 📚 บทเรียนและ code อยู่ใน repository นี้ 44 | - แนะนำว่าให้เริ่มตั้งแต่บทที่ 0 (พยายามอย่าข้าม) เนื้อหาตัดมาให้แล้วแต่เนื้อๆ คลิปละ 10-15 นาที ถ้าอยากได้น้ำด้วยไปติดตามในเพจ [React ไปวันๆ](https://www.facebook.com/devMasterSomeday/) นะคับ 45 | - แนะนำให้ทำแบบฝึกหัดในตอนท้ายของวิดีโอ (ถ้ามี) เพราะถ้าอยากเข้าใจด้วยตัวเองจริงๆต้องลงมือทำ ผมช่วยได้แค่แนะนำและเล่าประสบการณ์ให้ฟังเท่านั้น 46 | - เนื่องจากวิธีการสอนเน้นความเข้าใจ ในวิดีโอจะพูดถึงจากปัญหาหรือสิ่งที่ต้องการก่อน จากนั้นค่อยนำเครื่องมือ(react API) ที่เหมาะสมเข้ามาใช้ 47 | - syntax ทั้งหมด จะเขียนด้วย ES6 ขึ้นไป 48 | - ส่วนใหญ่ผมจะพูดไทยบ้างอังกฤษบ้างเพราะศัพท์บางคำใช้ภาษาอังกฤษจะดีกว่า (ถ้ามีคำหยาบ ต้องขออภัย 🙏) 49 | - ภาษาที่ใช้ในการพูดหรือพิม อาจมีความผิดพลาดได้ (ส่วนใหญ่จะไม่ได้เป็นทางการ) หากในส่วนไหนมีความผิดพลาดสามารถเปิด PR เข้ามาได้เลยครับ 50 | - โปรดอย่าคาดหวังเรื่อง production ในการตัดต่อวิดีโอนะครับ 🤣 เป้าหมายคือให้พอดูได้ 51 | 52 | ## ค่าใช้จ่าย 53 | 54 | คอร์สนี้ **Free** ทั้งหมด 55 | 56 | ### !!!ห้ามนําไปใช้เชิงพาณิชย์ เด็ดขาด!!! 57 | 58 | ## เครื่องมือและการติดตั้ง 59 | 60 | - [clone โปรเจคลงในเครื่อง](https://github.com/React-in-Thai/learn-react-foundation.git) 61 | - เปิดด้วย [VSCode](https://code.visualstudio.com/download) (IDE อื่นสามารถใช้ได้เช่นกัน) 62 | - ติดตั้ง `serve` ด้วย yarn หรือ npm 63 | ```bash 64 | yarn global add serve 65 | ``` 66 | or 67 | ```bash 68 | npm install -g serve 69 | ``` 70 | - เปิดโปรเจคขึ้นมาแล้วรัน `serve` ได้เลย 71 | 72 | > คิดว่ารองรับทั้ง mac และ window นะครับ ถ้าติดปัญหาอะไรลองหาวิธีแก้ด้วยตัวเองก่อน(เพราะคุณจะต้องเจออีกเยอะ) แต่ถ้าไม่ได้จริงๆให้เปิด [issue](https://github.com/React-in-Thai/learn-react-foundation/issues) ไว้ 73 | 74 | ## โครงสร้าง repo 75 | 76 | - สคริปตอนพูดในวิดีโอจะอยู่ในโฟลเดอร์ `/lessons/$lesson_number/$lesson_number.md` (สคริปจะไม่ตรงกับในวิดีโอเป๊ะๆ) 77 | 78 | --- 79 | 80 |
81 | 82 | ◀ Previous Lesson 83 | 84 |  |  85 | 86 | Next Lesson ▶ 87 | 88 |
89 |
90 | 91 | >>> ไปหน้าสารบัญ <<< 92 | 93 |
94 | -------------------------------------------------------------------------------- /lessons/L03_managing-state/solution.js: -------------------------------------------------------------------------------- 1 | function App() { 2 | const [title, setTitle] = React.useState(""); 3 | const [content, setContent] = React.useState(""); 4 | const [history, setHistory] = React.useState([]); 5 | const [future, setFuture] = React.useState([]); 6 | const [notes, setNotes] = React.useState([]); 7 | const [selectedNote, setSelectedNote] = React.useState(null); 8 | return ( 9 |
12 |
19 | {notes.map((note, index) => ( 20 |
21 |

22 | {note.title} 23 |

24 |

25 | {(note.content || "").slice(0, 40)} 26 |

27 |
28 | 37 | 47 |
48 |
49 | ))} 50 |
51 |
52 |

Note Editor

53 | 54 | 55 |
56 | { 60 | setTitle(event.target.value); 61 | setHistory((latestHistory) => [ 62 | { title, content }, 63 | ...latestHistory, 64 | ]); 65 | }} 66 | /> 67 |
68 |
69 | 70 | 71 |
72 |