Chào mọi người, sau bài viết 1 ít kinh nghiệm nhỏ về lifecycle trong React Native của mình vào lần trước thì hôm nay mình cũng muốn chia sẻ tiếp với mọi người về Hook trong React. Đây tiếp tục là kinh nghiệm của mình từ khi chuyển hoàn toàn qua Hook thay cho lifecycle.
Có nhiều bài viết nói rằng componentDidMount() chính là useEffect(() => {}, []), nhưng sự thật không phải như vậy, vì cách hoạt động của chúng khác nhau nhưng lại vô tình cho ra kết quả giống nhau. Mà, ban đầu nếu mới chuyển qua từ lifecycle thì hiểu vậy cũng được, nhưng nên bỏ suy nghĩ này sớm vì:
- componentDidMount() được gọi khi UI được vẽ ra xong.
- useEffect(() => {}, []) được gọi 1 lần duy nhất vì dependencies rỗng.
=> Rõ ràng ý nghĩa của 2 cái này khác nhau.
Tương tự với componentDidUpdate và useEffect có dependencies khác rỗng.
Tóm lại: useEffect được gọi khi item trong dependencies thay đổi.
Trong một component, bạn có thể khai báo nhiều useEffect, cho nên việc quản lý response từ server cũng rõ ràng hơn, không cần phải gôm tất cả vào một cái componentDidUpdate như trước nữa.
VD: bạn có gọi một API lấy thông tin người dùng, sẽ có 3 action là GETTING, GET_SUCCESS, GET_FAIL tương ứng với 3 state là loading?, userInfo {}, error?. Bạn có thể đưa 3 thằng này vào 3 cái useEffect khác nhau: useEffect(() => {setLoading(loading)}, [loading]), …
Bạn thấy đó, trong useEffect có sử dụng “loading” nên trong dependencies phải thêm loading vào. Để khi loading thay đổi => useEffect được gọi. Ở cái useEffect trên không nhắc gì đến userInfo hay error cả, nên cứ thoải mái mà tách chúng ra thành 2 cái useEffect nếu cần thiết.
Tóm lại: chỉ thêm những biến nào có dùng trong useEffect vào dependencies, đừng thêm dư cũng đừng thêm thiếu, hãy thêm đủ
Trong react-redux, nó có cho mình 2 cái là hook là useSelector và useDispatch thay thế cho mapStateToProps và mapDispatchToProps.
Trước đây mọi lần muốn dispatch action phải khai báo rất dài dòng: import action, rồi đưa nó vào mapStateToProps, rồi mới sử dụng this.pops.actionName(), tương tự với mapStateToProps.
Còn bây giờ chỉ cần import action, rồi useDispatch để gọi luôn cái action đó, đơn giản dễ hiểu, chả cần phải map này nọ nữa. Đỡ tốn được biết bao nhiêu dòng code.
Tóm lại: từ bây giờ hãy sử dụng useDispatch và useSelector thay cho 2 thằng map để code nhìn gọn hơn và dễ hiểu hơn.
Khi ở component Cha bạn sử dụng 1 function làm props cho 1 component Con để callback, thì hãy nhớ cho nó thêm 1 cái useCallback, vì khi component Cha re-render -> function callback đó cũng sẽ có giá trị con trỏ mới -> cho nên component Con thay đổi -> re-render Con.
(Bạn cũng nên tìm hiểu thêm về vấn đề capture của Hook và đây là link để bạn tìm hiểu và hơn thế nữa: https://overreacted.io/a-complete-guide-to-useeffect/)
Tóm lại: function callback cũng là props, props thay đổi thì component sẽ re-render.
Thật ra thì có thể có, có thể không, tùy mọi người, còn câu trả lời của mình là có. (và mình đang dùng Redux-Saga)
Redux ngoài việc quản lý state cho cả project, để khỏi phải truyền qua truyền lại giữa nhiều component thì nó cũng là chỗ quản lý logic code khá tốt.
Khi request 1 API trong saga, response cũng trong saga, rồi mình làm gì đó với response đó để có format mà mình mong muốn để khi component lấy ra xài thì xài luôn.
Mình từng thấy 1 comment nôm na là, tại sao phải để logic ở trong redux mà không để ở component luôn? Rõ ràng là được, mình có thể request API và nhận response ở trên component, đâu sao cả.
Nhưng khi có lỗi xảy ra, thì nó là cả 1 vấn đề. Nếu lúc thì ở component, lúc thì ở redux, thì khi gặp bug, bạn phải tìm cả 2 chỗ, tốn thời gian, trong khi đó nếu để tất cả ở component hoặc redux thì bạn sẽ biết vào đâu mà tìm ngay. Nhưng để ở component sẽ khiến cho code mình dài thêm, trong 1 file js thì nên giới hạn ở khoảng 100-200 dòng, nếu dài quá sẽ khó fix bug lắm, nên theo ý kiến cá nhân mình thì nên tách logic ra khỏi UI là đẹp.
Trên đây là 1 số kinh nghiệm về Hook của mình, đây chỉ là 1 bài sharing, nên có thể đúng hoặc sai, nếu bạn có ý kiến khác hay chỗ nào đó chưa hợp lý thì hãy comment để cho mình và mọi người học hỏi thêm. Code là không ngừng học hỏi và tiến bộ.
1. Lifecycle và useEffect
- componentDidMount() được gọi khi UI được vẽ ra xong.
- useEffect(() => {}, []) được gọi 1 lần duy nhất vì dependencies rỗng.
=> Rõ ràng ý nghĩa của 2 cái này khác nhau.
Tương tự với componentDidUpdate và useEffect có dependencies khác rỗng.
Tóm lại: useEffect được gọi khi item trong dependencies thay đổi.
2. Hook và API
VD: bạn có gọi một API lấy thông tin người dùng, sẽ có 3 action là GETTING, GET_SUCCESS, GET_FAIL tương ứng với 3 state là loading?, userInfo {}, error?. Bạn có thể đưa 3 thằng này vào 3 cái useEffect khác nhau: useEffect(() => {setLoading(loading)}, [loading]), …
Bạn thấy đó, trong useEffect có sử dụng “loading” nên trong dependencies phải thêm loading vào. Để khi loading thay đổi => useEffect được gọi. Ở cái useEffect trên không nhắc gì đến userInfo hay error cả, nên cứ thoải mái mà tách chúng ra thành 2 cái useEffect nếu cần thiết.
Tóm lại: chỉ thêm những biến nào có dùng trong useEffect vào dependencies, đừng thêm dư cũng đừng thêm thiếu, hãy thêm đủ
3. Hook và Redux
Trước đây mọi lần muốn dispatch action phải khai báo rất dài dòng: import action, rồi đưa nó vào mapStateToProps, rồi mới sử dụng this.pops.actionName(), tương tự với mapStateToProps.
Còn bây giờ chỉ cần import action, rồi useDispatch để gọi luôn cái action đó, đơn giản dễ hiểu, chả cần phải map này nọ nữa. Đỡ tốn được biết bao nhiêu dòng code.
Tóm lại: từ bây giờ hãy sử dụng useDispatch và useSelector thay cho 2 thằng map để code nhìn gọn hơn và dễ hiểu hơn.
4. Hook và Rendering
(Bạn cũng nên tìm hiểu thêm về vấn đề capture của Hook và đây là link để bạn tìm hiểu và hơn thế nữa: https://overreacted.io/a-complete-guide-to-useeffect/)
Tóm lại: function callback cũng là props, props thay đổi thì component sẽ re-render.
5. Có nên dùng Redux không?
Redux ngoài việc quản lý state cho cả project, để khỏi phải truyền qua truyền lại giữa nhiều component thì nó cũng là chỗ quản lý logic code khá tốt.
Khi request 1 API trong saga, response cũng trong saga, rồi mình làm gì đó với response đó để có format mà mình mong muốn để khi component lấy ra xài thì xài luôn.
Mình từng thấy 1 comment nôm na là, tại sao phải để logic ở trong redux mà không để ở component luôn? Rõ ràng là được, mình có thể request API và nhận response ở trên component, đâu sao cả.
Nhưng khi có lỗi xảy ra, thì nó là cả 1 vấn đề. Nếu lúc thì ở component, lúc thì ở redux, thì khi gặp bug, bạn phải tìm cả 2 chỗ, tốn thời gian, trong khi đó nếu để tất cả ở component hoặc redux thì bạn sẽ biết vào đâu mà tìm ngay. Nhưng để ở component sẽ khiến cho code mình dài thêm, trong 1 file js thì nên giới hạn ở khoảng 100-200 dòng, nếu dài quá sẽ khó fix bug lắm, nên theo ý kiến cá nhân mình thì nên tách logic ra khỏi UI là đẹp.
Trên đây là 1 số kinh nghiệm về Hook của mình, đây chỉ là 1 bài sharing, nên có thể đúng hoặc sai, nếu bạn có ý kiến khác hay chỗ nào đó chưa hợp lý thì hãy comment để cho mình và mọi người học hỏi thêm. Code là không ngừng học hỏi và tiến bộ.