React.createElement()
const element = React.createElement('div', { className: 'container' }, 'Hello, World!');
React.createElement(type,{props},children);type: loại phần tử HTML (h1,p, nút, v.v.).props: thuộc tính của đối tượng ({style:{size:10px}} hoặc Eventhandlers, classNames, v.v.).children: bất cứ thứ gì cần được bao bọc bởi thành phần đó.
JSX là một phần của React, nó là một cú pháp mở rộng của JavaScript cho phép bạn viết các cấu trúc giống HTML trong mã JavaScript. Nó giúp làm cho việc tạo và quản lý giao diện người dùng trong React trở nên dễ dàng và rõ ràng hơn.
const element = <div>Hello, World!</div>;<type {props} >{children}</typeprops là một cách để truyền dữ liệu từ một thành phần cha đến một thành phần con.
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
const element = <Welcome name="Kelvin" />;Node.js là một nền tảng runtime được xây dựng trên JavaScript, sử dụng trình thông dịch JavaScript V8 của Google.
Webpack: Là một công cụ mạnh mẽ cho việc đóng gói (bundling) mã nguồn JavaScript, CSS, hình ảnh và các tài nguyên khác trong ứng dụng web của bạn. Nó cho phép bạn quản lý và tối ưu hóa mã nguồn của mình bằng cách tạo ra các bundle (gói) tối ưu để tải xuống
NPM (Node Package Manager) là một công cụ quản lý gói (package manager) cho Node.js và môi trường JavaScript. Nó được sử dụng để tải xuống, cài đặt, quản lý và chia sẻ các thư viện, công cụ và tài nguyên khác cho phát triển ứng dụng JavaScript.
NPX là một công cụ đi kèm với Node.js từ phiên bản 5.2.0 trở lên. Nó được sử dụng để chạy các công cụ hoặc thực thi các gói npm mà không cần cài đặt chúng cục bộ trên máy tính của bạn.
YARN là một công cụ quản lý gói (package manager) cho JavaScript, tương tự như npm (Node Package Manager). Yarn được phát triển bởi Facebook, Google, Exponent và Tilde. Nó cung cấp một cách hiệu quả và nhanh chóng để quản lý các phụ thuộc (dependencies) và các gói trong các dự án JavaScript.
Hook giúp bạn tái sử dụng logic, giảm thiểu lặp lại mã, và làm cho các thành phần hàm trở nên mạnh mẽ và linh hoạt hơn.
useState: Hook này cho phép bạn sử dụng trạng thái trong thành phần hàmuseEffect: Hook này giúp bạn thực hiện các tác vụ sau khi thành phần đã render hoặc sau mỗi lần renderuseContext: Cho phép bạn sử dụng Context trong các thành phần hàm.useReducer: Tương tự như useState, nhưng sử dụng một hàm reducer để quản lý trạng thái phức tạp hơnuseMemovàuseCallback: Được sử dụng để tối ưu hóa hiệu suất. useMemo giúp bạn lưu kết quả của một hàm được tính toán và chỉ tính toán lại khi các giá trị dependencies thay đổi. useCallback giúp tránh việc tạo lại các hàm callback mỗi khi render.
const [counter, setCounter] = useState(1);
const handleIncrease = () => {
setCounter(counter + 1);
setCounter(prevSate => prevSate + 1);
}
return (
<div className="App" style={{textAlign:"center"}}>
<h1 > {counter}</h1>
<button onClick = {handleIncrease}>+</button>
</div>
);Two-way binding trong React
const courses = [
{
id : 1,
name : 'HTML, CSS, JS'
},
{
id : 2,
name : 'ReactJS'
},
{
id : 3,
name : 'NodeJS'
}
]
const [checked, setChecked] = useState([])
const handleCheck = (id) => {
setChecked(prev => {
if (prev.includes(id)) {
return prev.filter(item => item !== id)
}
return [...prev, id]
})
}
const handleSubmit = () => {
console.log({ids: checked})
}
return (
<div className="App" style={{textAlign:"center"}}>
{courses.map(course => (
<div key = {course.id}>
<input
type = "radio"
type = "checkbox"
checked={checked.includes(course.id)}
onChange = {() => handleCheck(course.id)}
/>
{course.name}
</div>
))}
<button onClick={handleSubmit}>Register</button>
</div>
)Todolist with useState
const storageJobs = localStorage.getItem('jobs')
const [job, setJob] = useState('')
const [jobs,setJobs] = useState([])
const handleSubmit = () => {
setJobs(prev => {
const newJobs= [...prev, job]
const jsonJobs = JSON.stringify(newJobs)
localStorage.setItem('jobs', jsonJobs)
return newJobs
})
setJob('')
}
return (
<div className="App" style={{padding : 32}}>
<input
value={job}
onChange={e => setJob(e.target.value)}
/>
<button onClick = {handleSubmit}>ADD</button>
<ul>
{jobs.map((job, index) => (
<li key={index}>{job}</li>
))}
</ul>
</div>
)Mounted & Unmounted
const[show, setShow] = useState(false)
return (
<div className="App" style={{textAlign:"center"}}>
<button onClick = {() => setShow(!show)}>Show</button>
{show && <Content/>}
</div>
)useEffect with dependencies
const [title, setTitle] = useState('')
const [posts, setPosts] = useState([])
const [type, setType] = useState('posts')
const [showGoToTop, setShowGoToTop] = useState(false)
useEffect(() => {
fetch('https://jsonplaceholder.typicode.com/posts')
.then(response => response.json())
.then(posts => {
setPosts(posts);
})
fetch(`https://jsonplaceholder.typicode.com/${type}`)
.then(response => response.json())
.then(posts => {
setPosts(posts);
})
}, [type])
useEffect(() => {
const handleScroll = () => {
if (window.scrollY >= 200) {
setShowGoToTop(true)
} else {
setShowGoToTop(false)
}
}
window.addEventListener('scroll', handleScroll)
return () => {
window.removeEventListener('scroll', handleScroll)
}
},[])
return (
<div>
{tabs.map(tab => (
<button
key={tab}
style={type === tab ? {color: 'red'} : {color: 'black'}}
onClick={() => setType(tab)}
>
{tab}
</button>
))}
<input
value = {title}
onChange = {e => setTitle(e.target.value)}
/>
<ul>
{posts.map(post => (
<li key = {post.id}>{post.title || post.name}</li>))}
{showGoToTop && (
<button
style={{position: 'fixed', right: 20, bottom: 20}}
>Go To Top</button>
)}
</ul>
</div>
);useEffect with DOM events
const [width,setWidth] = useState(window.innerWidth)
useEffect(() => {
const handleResize = () => {
setWidth(window.innerWidth)
}
window.addEventListener('resize', handleResize)
return () => {
window.removeEventListener('resize', handleResize)
}
},[])
return (
<div>
<h1>{width}</h1>
</div>
)useEffect with timer functions
setInterval(() => {
setCountdown(coutndown - 1)
}, 1000)
useEffect(() => {
const timer = setInterval(() => {
setCountdown(prevState => prevState - 1)
}, 1000)
return () => clearInterval(timer)
}, [])
return (
<div>
<h1>{coutndown}</h1>
</div>
)useEffect with preview avatar
const [avatar, setAvatar] = useState()
useEffect(() => {
//Clear up
return () => {
avatar && URL.revokeObjectURL(avatar.preview)
}
},[avatar])
const handlePreviewAvatar = (e) => {
const file = e.target.files[0]
file.preview = URL.createObjectURL(file)
setAvatar(file)
}
return (
<div>
<input
type = "file"
onChange= {handlePreviewAvatar}
/>
{avatar && (
<img src = {avatar.preview} alt = "" width = "80%"/>
)}
</div>
)useEffect with fake Chat App
const lessons = [
{
id: 1,
name: "ReactJS là gì? Tại sao nên học ReactJS?",
},
{
id: 2,
name: "SPA/MPA là gì?",
},
{
id: 3,
name: "Ưu điểm của SPA",
},
];
const [room, setRoom] = useState(1);
useEffect(() => {
const handleEvent = (e) => {
console.log(e.detail);
};
window.addEventListener(`lesson-${room}`, handleEvent);
return () => {
window.removeEventListener(`lesson-${room}`, handleEvent);
};
}, [room]);
return (
<div className="main">
<ul>
{lessons.map((lesson) => (
<div key={lesson.id}>
<li
style={room === lesson.id ? { color: "red" } : {}}
onClick={() => setRoom(lesson.id)}
>
{lesson.id}. {lesson.name}
</li>
</div>
))}
</ul>
</div>
);useEffect
-
- Cập nhật lại state
-
- Cập nhật DOM (mutated)
-
- Render lại UI
-
- Gọi cleanup nếu deps thay đổi
-
- Gọi useEffect callback
useLayoutEffect
-
- Cập nhật lại state
-
- Cập nhật DOM (mutated)
-
- Gọi cleanup nếu deps thay đổi (sync)
-
- Gọi useLayoutEffect callback (sync)
-
- Render lại UI
const [count, setCount] = useState(0);
useLayoutEffect(() => {
if( count > 3 )
setCount(0)
}, [count])
const handleRun = () => {
setCount(count + 1)
}
return (
<div>
<h1>{count}</h1>
<button onClick={handleRun}>Run</button>
</div>
)useRef
useRef trong React được sử dụng để tạo một tham chiếu (reference) đến một phần tử DOM hoặc một giá trị có thể thay đổi mà không gây ra việc render lại lại component khi giá trị thay đổi.
const [count, setCount] = useState(60);
const timerId = useRef();
const prevCount = useRef();
useEffect(() => {
prevCount.current = count;
},[count])
const handleStart = () => {
timerId.current= setInterval(() => {
setCount((prevCount) => prevCount - 1)
},1000)
}
const handleStop = () => {
clearInterval(timerId.current);
}
return (
<div>
<h1 style={{padding: 20}}>{count}</h1>
<button onClick = {handleStart}>Start</button>
<button onClick = {handleStop}>Stop</button>
</div>
)React.memo HOC
import {memo} from "react";
function Content() {
return (
<div>
<h1>Content</h1>
</div>
)
}
export default memo(Content)
//Khi sử dụng memo
return (
<div>
<Content />
<h1 style={{padding: 20}}>{count}</h1>
<button onClick={increase}>Increase</button>
</div>
)useCallback dùng để giúp tránh tạo ra những hám mới một cách không cần thiết trong function component
useMemo giúp tránh thực hiện lại một logic nào đó không cần thiết
const [name,setName] = useState('');
const [price,setPrice] = useState('');
const [products,setProducts] = useState([])
const nameRef = useRef();
const handleSubmit = () => {
setProducts ( [...products, {
name,
price : +price
}])
setName ( '')
setPrice ('')
nameRef.current.focus()
}
const total = useMemo(() => {
const result = products.reduce ((result, prod) =>{
return result + prod.price
},0)
return result
},[products])
return (
<div style={{padding: '10px 32px'}}>
<input
ref={nameRef}
value= {name}
placeholder="Enter name ..."
onChange={e => setName(e.target.value)}
/>
<br />
<input
value={price}
placeholder='Enter price...'
onChange={e => setPrice(e.target.value)}
/>
<br />
<button onClick={handleSubmit}>Submit</button>
<br />
Total price :{total}
<ul>
{products.map((product,index) => (
<li key={index}>{product.name} - {product.price}</li>
))}
</ul>
</div>
)
useReducer Cung cấp cho người dùng có thêm một sự lựa chọn để sử dụng state cho function component
useState
-
- Init state : 0
-
- Actions : Up(state + 1), Down(state - 1)
useReducer
-
- Init state : 0
-
- Actions : Up(state + 1), Down(state - 1)
-
- Reducer
-
- Dispatch
//init
const initState = 0
// actions
const UP_ACTION = 'up'
const DOWN_ACTION = 'down'
//reducer
const reducer = (state, action) => {
switch(action) {
case UP_ACTION:
return state + 1
case DOWN_ACTION:
return state - 1
default:
throw new Error('Invalid action')
}
}
const [count, dispatch] = useReducer(reducer, initState)
return (
<div style={{padding: ' 20px'}}>
<h1>{count}</h1>
<button
onClick={() => dispatch(UP_ACTION)}
>Up
</button>
<button onClick={() => dispatch(DOWN_ACTION)}
>Down
</button>
</div>
)Todo App with useReducer hook
const initState = {
job: '',
jobs: []
}
// 2. Actions
const SET_JOB = 'set_job'
const ADD_JOB = 'add_job'
const DELETE_JOB = 'delete_job'
const setJob = payload => {
return {
type : SET_JOB,
payload
}
}
const addJob = payload => {
return {
type : ADD_JOB,
payload
}
}
const deleteJob = payload => {
return {
type : DELETE_JOB,
payload
}
}
// 3 . Reducer
const reducer = (state, action) => {
let newState
switch (action.type) {
case SET_JOB:
newState = {
...state,
job: action.payload
}
break;
case ADD_JOB:
newState = {
...state,
// job: '',
jobs: [...state.jobs, action.payload]
}
break;
case DELETE_JOB:
const newJobs = [...state.jobs]
newJobs.splice(action.payload,1)
newState = {
...state,
jobs: newJobs
}
break;
default:
throw new Error('Invalid action')
}
return newState
}
function App() {
const [state,dispatch] = useReducer(reducer,initState)
const {job,jobs} = state
const inputRef = useRef()
const handleSubmit = () => {
dispatch(addJob(job))
dispatch(setJob(''))
inputRef.current.focus()
}
return (
<div style= {{padding: ' 0 20px'}}>
<h3>Todo</h3>
<input
ref={inputRef}
value = {job}
placeholder="Enter todo..."
onChange = { e => {
dispatch(setJob(e.target.value))
}}
/>
<button onClick = {handleSubmit} >Add</button>
<ul>
{jobs.map((job, index) => (
<li key={index}>{job}
<button onClick={() => dispatch(deleteJob(index))}>
×
</button>
</li>
))}
</ul>
</div>
)
}useContext đơn giản hoá việc truyền dữ liệu từ component cha xuống component con mà không cần sử dụng tới props
import{useRef,useState,useEffect,useCallback, useMemo,memo,useReducer,createContext} from 'react';
import Content from './Content'
import './App.css'
// import TodoApp from './Todo'
export const ThemeContext = createContext()
function App () {
const[ theme, setTheme] = useState('dark')
const toggleTheme = () => {
setTheme(theme === 'dark' ? 'light' : 'dark')
}
return (
<ThemeContext.Provider value={theme}>
< div style={{padding : 20}}>
<button onClick={toggleTheme}>Toggle theme</button>
<Content/>
</div>
</ThemeContext.Provider>
)
// return <TodoApp />;
}
export default App;import { useEffect, useLayoutEffect, useState } from "react";
import {memo} from "react";
import Paragraph from "./Paragraph";
function Content() {
return (
<div>
<Paragraph />
</div>
)
}import { useContext } from "react"
import { ThemeContext } from "./App"
function Paragraph() {
const theme = useContext(ThemeContext)
return (
<p className= {theme}>
Tran Anh Quoc Dep Trai
</p>
)
}
export default ParagraphCompA => CompB => CompC
- 1.Create context
- 2.Provider
- 3.Consumer
Context + useReducer
import {useStore, actions} from './store'
function App () {
const [state, dispatch] =useStore()
const {todos, todoInput} = state
const handleAdd = () => {
dispatch(actions.addTodo(todoInput))
}
return (
<div>
<input
value = {todoInput}
placeholder='Enter Todo...'
onChange={e => {
dispatch(actions.setTodoInput(e.target.value))
}}
/>
<button style={{padding: '10px 10px'}} onClick = {handleAdd}>Add</button>
{todos.map((todo, index)=> (
<li key={index}>{todo}</li>
))}
</div>
)
}
// Các phần còn lại đều ở file storeuseImperativeHandle giúp tùy chỉnh ref của một function component
import { useRef} from 'react'
// import { useImperativeHandle} from 'react';
import Video from './Video'
function App () {
const videoRef = useRef()
const handlePlay = () => {
videoRef.current.play()
}
const handlePause = () => {
videoRef.current.pause()
}
return (
<div>
<Video ref = {videoRef}/>
<button onClick={handlePlay}>Play</button>
<button onClick={handlePause}>Pause</button>
</div>
)
}
export default App;// import {useRef} from 'react'
import { forwardRef,useImperativeHandle, useRef } from 'react'
import video1 from './videos/video-1.mp4'
function Video(props, ref) {
const videoRef = useRef ()
// const videoRef = useRef()
useImperativeHandle(ref, () => ({
play() {
videoRef.current.play()
},
pause() {
videoRef.current.pause()
}
}))
return (
<video
// ref={ref}
ref = {videoRef}
src={video1}
/>
)
}
export default forwardRef(Video)CSS module
import Heading from './components/Heading'
import Paragraph from './components/Paragraph'
function App () {
return (
<div style={{padding : "0 32px"}}>
<Heading/>
<Paragraph/>
</div>
)
}
export default App; import styles from './Heading.module.css'
function Heading() {
return (
<h1 className={styles.heading}>
TRAN ANH QUOC
</h1>
)
}
export default Heading ;
import styles from './Paragraph.module.css'
function Paragraph () {
return (
<p className = {styles.paragraph}>From Uranus, DA NANG</p>
)
}
export default Paragraph