React hooks rất là tuyệt vời khi nó cho phép chúng ta chỉ sử dụng duy nhất functional component trong app của chúng ta. Và chúng ta có thể đặt logic và state bên trong function quá là tiện lợi. Hơn thế nữa nó cho phép chúng ta tạo custom hook và sử dụng lại khi cần thiết. Điều này khó làm được với class component. Very nice!!
Có một lưu ý là những function trong component sẽ được chạy lại sau mỗi lần render.
function Distance({x, y}) {
const distance = calculateDistance(x, y)
return (
<div>
The distance between {x} and {y} is {distance}.
</div>
)
}
Ví dụ hàm calculateDistance
luôn luôn được chạy lại khi component <Distance />
render lại vì một lý do gì đó. Cho dù kết quả của distance có bằng nhau nhưng nó vẫn bị chạy lại mỗi lần re-render. Ví dụ trong trường hợp component cha render lại thì component <Distance />
sẽ re-render.
Đó là lý do chúng ta sử dụng useMemo:
function Distance({x, y}) {
const distance = React.useMemo(() => calculateDistance(x, y), [x, y])
return (
<div>
The distance between {x} and {y} is {distance}.
</div>
)
}
React.useMemo
nhận vào hai tham số đó là một function với return value và một array chứa những tham số thay đổi của function đó. Với useMemo
mỗi lần render lại hàm calculateDistance
chỉ được thực thi khi mà một trong x,y
thay đổi, nếu không nó sẽ lấy kết quả trước đó vì với cùng một input (x,y) thì sẽ cho kết quả giống nhau (calculateDistance(x, y)) cho nên chúng ta sẽ chỉ lấy giá trị cũ. Điều này giúp tối ưu thời gian chạy của component, giả sử hàm calculateDistance
của bạn chạy 2-3s thì hiệu suất sẽ tăng đáng kể nếu sử dụng useMemo trong trường hợp này.
Tối ưu hàm getItems
Giả sử chúng ta có một danh sách rất nhiều các thành phố trong demo sau đây. Chúng ta có Menu
component nhận vào allItems
và sau mỗi lần render hàm getItems
được chạy lại. Hàm này chạy rất chậm vì nhận vào hàng ngàn item và sort chúng mỗi lần render. Điều này có thể làm App
component render rất chậm. Nhiệm vụ của bạn là sử dụng React.useMemo
để hạn chế chạy lại làm getItems
nếu giá trị inputValue
nó không thay đổi.
Hàm getItems
function getItems(filter) {
if (!filter) {
return allItems
}
return matchSorter(allItems, filter, { // filter và sort hàng ngàn items here
keys: ['name'],
})
}
function App() {
const forceRerender = useForceRerender()
const [inputValue, setInputValue] = React.useState('')
// 🐨 bọc getItems bên trong `React.useMemo`
const allItems = getItems(inputValue) // hàm này sort hàng ngàn items bên trong
const items = allItems.slice(0, 100)
const {
selectedItem,
highlightedIndex,
getComboboxProps,
getInputProps,
getItemProps,
getLabelProps,
getMenuProps,
selectItem,
} = useCombobox({
items,
inputValue,
onInputValueChange: ({inputValue: newValue}) => setInputValue(newValue),
onSelectedItemChange: ({selectedItem}) =>
alert(
selectedItem
? `You selected ${selectedItem.name}`
: 'Selection Cleared',
),
itemToString: item => (item ? item.name : ''),
})
return (
<div className="city-app">
<button onClick={forceRerender}>force rerender</button>
<div>
<label {...getLabelProps()}>Find a city</label>
<div {...getComboboxProps()}>
<input {...getInputProps({type: 'text'})} />
<button onClick={() => selectItem(null)} aria-label="toggle menu">
✕
</button>
</div>
<Menu
items={items}
getMenuProps={getMenuProps}
getItemProps={getItemProps}
highlightedIndex={highlightedIndex}
selectedItem={selectedItem}
/>
</div>
</div>
)
}
export default App
Đơn giản bạn sử dụng React.useMemo
như sau:
const allItems = React.useMemo(() => getItems(inputValue), [inputValue])
thaunguyen.com via Epic React by Kent C.Dodds