useSelector nhận selector function, subscribe vào Redux store, và dùng strict === comparison (reference equality) để quyết định có re-render không.
- Lưu ý quan trọng: nếu selector trả về object/array mới mỗi lần gọi — ví dụ
useSelector(s => s.items.filter(...))— reference luôn khác nên component re-render liên tục dù data không đổi. - Fix: dùng
shallowEqualtừ react-redux làm argument thứ hai (useSelector(selector, shallowEqual)) để so sánh từng property, hoặc dùngcreateSelector(Reselect) để memoize. useDispatch trả về stable dispatch reference — không bao giờ thay đổi giữa các render, an toàn khi đặt vào dependency array. - So sánh với connect() HOC cũ: connect() dùng shallowEqual mặc định nên ít gặp re-render issue hơn, nhưng code verbose (mapStateToProps, mapDispatchToProps) và wrap component trong HOC gây khó debug. useSelector/useDispatch code gọn hơn 50% nhưng developer phải tự xử lý equality.
Ví dụ pitfall thực tế: useSelector(s => ({ a: s.a, b: s.b })) tạo object mới mỗi render → infinite re-render loop khi có dispatch trong useEffect — fix bằng 2 useSelector riêng biệt hoặc shallowEqual.
useSelector takes a selector function, subscribes to the Redux store, and uses strict === comparison (reference equality) to decide whether to re-render.
- Important pitfall: if the selector returns a new object/array on every call — e.g.,
useSelector(s => s.items.filter(...))— the reference is always different, causing the component to re-render constantly even if data hasn't changed. - Fix: use
shallowEqualfrom react-redux as the second argument (useSelector(selector, shallowEqual)) to compare properties, or usecreateSelector(Reselect) for memoization. useDispatch returns a stable dispatch reference — it never changes between renders, making it safe to include in dependency arrays. - Compared to the old
connect()HOC: connect() uses shallowEqual by default so re-render issues are less common, but the code is verbose (mapStateToProps, mapDispatchToProps) and wraps components in HOCs that are harder to debug. useSelector/useDispatch reduces code by ~50% but developers must handle equality themselves. - Practical pitfall:
useSelector(s => ({ a: s.a, b: s.b }))creates a new object every render → potential infinite re-render loop if there's a dispatch in a useEffect — fix with two separate useSelectors or shallowEqual.