Khi học qua Redux
có thể bạn đã từng nghe "giữ state đơn giản nhất, và sử dụng nó khi cần', một phần trong bài học đó bạn cũng có thể đã sử dụng đến redux selector
.
Một selector function
nhận vào input
là state
và trả về một giá trị mong muốn dựa trên state
đó. ví dụ
const selectEntities = state => state.entities;
function selectItemIds(state) {
return state.items.map(item => item.id);
}
const selectSomeSpecificField = state => state.some.deeply.nested.field;
function selectItemsWhoseNamesStartWith(items, namePrefix) {
const filteredItems = items.filter(item => item.name.startsWith(namePrefix));
return filteredItems;
}
Bạn có thể sử dụng selector
ở khắp mọi nơi miễn sao nó là một component
, một selector
thường bắt đầu với prefix
get[something] hoặc select[something] hoặc có thể là một suffix
[something]Selector.
Việc render:
-
Khi một
action
đượcdispatch
,useSelector
sẽ thực hiện so sánh giữa kết quả trước đó và kết quả hiện tại, nếu khác, component bị force đểre-render
. -
useSelector
sử dụng so sánh===
chứ không dùng phương phápshallow compare
-
Lý do sử dụng
useSelector
:- Tái sử dụng,
selector
có thể được sử dụng ở nhiều nơi, nhiềucomponent
khác nhau mà không cần khai báo lại - Tinh gọn, chúng ta có một
state
car
chứaname
,brand
,year
, nếu muốn lấybrand
thì chỉ cần selectorgetBrandCar
là dễ hiểu - Cập nhật, khi
structure
củaredux store
thay đổi, chúng ta chỉ cần cập nhật lạiselector
là xong
// Previous ... store = { car: {id: 1, brand: "Vinfast", name: "Lux A", year: "2019"} } // selector export const getCarBrand = state => state.car.brand //==> After ... store = { car: {id: 2, company: "Vinfast", name: "Lux A", year: "2019"} } //selector export const getCarCompany = state => state.car.company
- Tái sử dụng,
Sử dụng:
Vì selector
sử dụng phương thức so sánh "===" nên nếu trả về array
, object
thông qua việc tính toán thì component sẽ bị trigger re-render
. Trong lúc re-render
cho dù data
không thay đổi, thì hàm selectFilteredSortedTransformedData
vẫn bị gọi lại:
const selectFilteredSortedTransformedData = state => {
const filteredData = expensiveFiltering(state.data);
const sortedData = expensiveSorting(filteredData);
const transformedData = expensiveTransformation(sortedData);
return transformedData;
}
Cách giải quyết vấn đề, sử dụng reselect:
const selectSomeData = state => state.someData;
const selectFilteredSortedTransformedData = createSelector(
selectSomeData,
(someData) => {
const filteredData = expensiveFiltering(someData);
const sortedData = expensiveSorting(filteredData);
const transformedData = expensiveTransformation(sortedData);
return transformedData;
})
Lưu ý:
-
Nếu
selector
cóinput
là 1prop
củacomponent
hãy đưaselector
ra ngoàicomponent
... const selectCar = (state, carId) => cars.find(car => car.id === carId); const selectCarById = createSelector( [selectCar], car => expensiveTransformation(car) ); export const CarDetails = ({ carId }) => { const car = useSelector(state => selectCarById(state, carId)); return <div>{car.name}</div> };
-
Nhiều
instance
củacomponent
: Khi mộtselector
được sử dụng trong nhiềuinstance
của mộtcomponent
và cũng phụ thuộc vàoprop
, VD:<CarDetails carId={1}> <CarDetails carId={2}>
Điều ta cần làm bây giờ là hãy chắc chắn mỗi
instance component
có mộtinstance selector
riêng biệt, như ví dụ ở trên thì không đúng vì 2instance
đều trỏ dùng chunginstance selector
.Giải pháp, sử dụng
useMemo
... const selectCar = (state, carId) => cars.find(car => car.id === carId); const selectCarById = createSelector( [selectCar], car => expensiveTransformation(car) ); export const CarDetails = ({ carId }) => { const selectCarMemo = useMemo(selectCarById, []) const car = useSelector(state => selectCarMemo(state, carId)); return <div>{car.name}</div> };
Và bây giờ mỗi instance của
CarDetails
sẽ có một instance củaselector
khác nhau