Tự tạo React custom hooks
Bài đăng này đã không được cập nhật trong 5 năm
1. Introduction
React Hook
là một tính năng mới cho phép bạn sử dụng state và các tính năng khác của React mà không cần phải viết class (functional component).- Cụ thể trong bài này mình sẽ giới thiệu 2 hook cơ bản đó là
useState
vàuseEffect
. React còn giới thiệu rất nhiều hook khác như làuseContext
,useReducer
,useCallback
,useMemo
,useRef
... Các bạn có thể tìm hiểu thêm ở trang chủ của React: Hook Reference.
2. Content
-
Để dễ dàng tìm hiểu về 2 hook ở trên thì mình sẽ cùng làm một ứng dụng note có các chức năng như là thêm mới và xoá note.
-
Đầu tiên chúng ta sẽ tạo một ứng dụng react mới, ở đây mình dùng Create React App để tạo ứng dụng react mới
create-react-app note-demo
-
Đầu tiên mình tạo 1 hook tên là useNoteState để thao tác với danh sách các note, như hiển thị, thêm note và xoá note.
// src/useNoteState.js import { useState } from 'react'; export default initialValue => { const [notes, setNotes] = useState(initialValue); return { notes, addNote: note => { setNotes([...notes, note]); }, }; };
-
Ở trên mình có sử dụng hook state là
useState
do react cung cấp, hook này trả về hai thứ, đó là giá trị của state hiện tại (notes: danh sách note) và hàm để thay đổi state đó (setNotes). Có thể gọi hàm setNotes này ở bất cứ đâu trong component, hàm này hoạt động tương tự như this.setState trong class component. -
Hàm
useState
chỉ nhận vào một tham số duy nhất đó là giá trị khởi tạo của state đó. Tham số này có thể là bất cứ giá trị nào bạn muốn set cho state đó, ở đây nó là danh sách note nên mình sẽ để là một mảng các note. -
Thông thường thì useState hay đặt ngay trong component và ở trên tất cả các hàm khác, ở đây vì mình muốn tách ra cho dễ hiểu nên tạo một file riêng và viết ở trong này, hàm useNoteState này mình đã custom lại ko chỉ trả về danh sách notes và hàm chỉnh sửa danh sách đó như mặc định mà mình đặt tên nó là addNote cho dễ hiểu.
-
Tiếp theo ở component App mình sẽ import
useNoteState
vào và sử dụng nó tương tự nhưuseState
như sau:// src App.js import React from 'react'; import useNoteState from './useNoteState'; const App = () => { const { notes, addNote } = useNoteState([]); return ( <div className="App"> <h1>Notes</h1> </div> ); }; export default App;
-
Tiếp theo mình sẽ tạo một component tên là
NoteList
để render ra danh sách các notes.// src/NoteList.js import React from 'react'; const NoteList = ({ notes }) => { return ( <ul> {notes.map(note => { return <li>{note}</li>; })} </ul> ); }; export default NoteList;
-
Ở component App mình sẽ sửa lại như sau:
// src/App.js import React from 'react'; import NoteList from './NoteList'; import useNoteState from './useNoteState'; const App = () => { const { notes, addNote } = useNoteState([]); return ( <div className="App"> <h1>Notes</h1> <NoteList notes={notes} /> </div> ); }; export default App;
-
Có hiển thị rồi giờ mình sẽ tạo một form để thêm note vào, trước khi tạo form mình sẽ tạo một hook state để sử dụng trong form, bởi ở đây có value của ô input sẽ thay đổi liên tục đòi hỏi mình phải custom lại hook state đó.
-
Mình sẽ tạo một custom hook tên là
useInputState
như sau:// src/useInputState import { useState } from 'react'; export default () => { const [value, setValue] = useState(''); return { value, onChange: event => { setValue(event.target.value); }, reset: () => setValue(''), }; };
-
Ở đây mình đã custom thêm hàm
reset
cũng nhưonChange
để trongNoteForm
mình sẽ sử dụng để map vào hàm onChange của input cũng như sau khi add thêm sẽ reset value về rỗng. -
Tiếp theo mình tạo component
NoteForm
để tạo form thêm note như sau:import React from 'react'; import useInputState from './useInputState'; const NoteForm = ({ saveNote }) => { const { value, onChange, reset } = useInputState(); return ( <form onSubmit={event => { event.preventDefault(); saveNote(value); reset(); }} > <input placeholder="Add note" onChange={onChange} value={value} /> </form> ); }; export default NoteForm;
-
Ở đây khi form đc submit thì mình sẽ gọi đến props saveNote và gửi kèm value của ô input để lưu lại vào danh sách notes, nên ở component App mình sẽ phải khai báo hàm
saveNote
và trong hàmsaveNote
sẽ gọi hàmaddNote
của hookuseNoteState
. Mình sẽ update lại component App như sau:// src/App.js import React from 'react'; import NoteList from './NoteList'; import NoteForm from './NoteForm'; import useNoteState from './useNoteState'; const App = () => { const { notes, addNote } = useNoteState([]); return ( <div className="App"> <h1>Notes</h1> <NoteForm saveNote={note => { const trimmedText = note.trim(); if (trimmedText.length > 0) addNote(trimmedText); }} /> <NoteList notes={notes} /> </div> ); }; export default App;
-
Sau đó sẽ ra đc giao diện như sau:
-
Tiếp đến chúng ta sẽ làm chức năng xoá note. Đầu tiên mình update lại file
useNoteState
để thêm một actiondeleteNote
như sau:// src/useNoteState.js import { useState } from 'react'; export default initialValue => { const [notes, setNotes] = useState(initialValue); return { notes, addNote: note => { setNotes([...notes, note]); }, deleteNote: noteIndex => { const newList = notes.filter((_, index) => index !== noteIndex); setNotes(newList); }, }; };
-
Tiếp theo update component App:
// src/App.js import React from 'react'; import NoteList from './NoteList'; import NoteForm from './NoteForm'; import useNoteState from './useNoteState'; const App = () => { const { notes, addNote, deleteNote } = useNoteState([]); return ( <div className="App"> <h1>Notes</h1> <NoteForm saveNote={note => { const trimmedText = note.trim(); if (trimmedText.length > 0) addNote(trimmedText); }} /> <NoteList notes={notes} deleteNote={deleteNote} /> </div> ); }; export default App;
-
Cuối cùng ta update file
NoteList
:// src/NoteList.js import React from 'react'; const NoteList = ({ notes, deleteNote }) => { return ( <ul> {notes.map((note, index) => { return ( <> <li key={index.toString()}>{note}</li> <span> </span> <button onClick={() => { deleteNote(index); }} > Delete </button> </> ); })} </ul> ); }; export default NoteList;
-
Cuối cùng thì ta ra được giao diện như này
-
Tiếp theo mình muốn hiển thị số lượng note đang có lên title của tab, mình sử dụng
hook effect
, mình sửa file App.js như sau:// src/App.js import React, { useEffect } from 'react'; import NoteList from './NoteList'; import NoteForm from './NoteForm'; import useNoteState from './useNoteState'; const App = () => { const { notes, addNote, deleteNote } = useNoteState([]); useEffect(() => { document.title = `You have ${notes.length} notes`; }); return ( <div className="App"> <h1>Notes</h1> <NoteForm saveNote={note => { const trimmedText = note.trim(); if (trimmedText.length > 0) addNote(trimmedText); }} /> <NoteList notes={notes} deleteNote={deleteNote} /> </div> ); }; export default App;
-
Hook
useEffect
sẽ thay thế cho các life cycle method nhưcomponentDidMount
,componentDidUpdate
,componentWillUnmount
. Nó sẽ được gọi khi có thay đổi trong DOM, ở đây mỗi khi danh sách notes đc add thêm note vào thì hàmuseEffect
sẽ được gọi và cập nhật lên title của tab. -
Và ở title tab của trình duyệt sẽ hiển thị như sau:
-
Ta đã có thể thêm và xoá được note một cách rất đơn giản. Do đây là bài giới thiệu nên giao diện mình làm rất là basic, nếu muốn các bạn có thể thêm css vào cho đẹp mắt
3. Conclusion
- Trong bài này mình đã giới thiệu với các bạn hai hook cơ bản của React đó là
useState
vàuseEffect
. Ngoài hai hook này thì React vẫn còn khá nhiều hook khác nữa, trong các bài sau mình sẽ tiếp tục tìm hiểu về các hook này và giới thiệu với các bạn thêm. - Các bạn có thể tham khảo repo của mình: React hook demo
4. References
All rights reserved