REACTJS , Làm sao để trigger một Function từ một component khác không có quan hệ gì
Chào các bạn, mình có vấn đề này nhờ mn giúp đỡ.
Tại comp PostForm
, mình muốn khi tạo một bài viết mới xong sẽ gọi lại hàm getPostList
bên component PostList
thì làm thế nào ạ.
https://codesandbox.io/s/determined-meadow-epy4p
Cám ơn mn đã đọc bài viết của m !
4 CÂU TRẢ LỜI
Ý tưởng ở đây đơn giản là gọi lại hàm thì bạn có thể tạo ra 1 listener ở bên PostList và trigger nó ở bên PostForm. Có rất nhiều thư viên hỗ trợ làm trò này: https://github.com/facebook/emitter https://nodejs.org/api/events.html
trong sandbox của bạn thì 2 component đang ko có liên hệ gì, về lý thuyết là không thể nhé, tuy nhiên mình giả sử bạn sẽ render 2 component PostForm và PostList trong component App thì sẽ giải quyết như sau:
- Như mình nói thì 2 Component sẽ cần có mối liên hệ, ở đây 2 component đều được render trong component App, thì đó chính là mối liên hệ, function getPostList thay vì đặt trong component PostList thì đặt ở trong component App, và truyền function đó xuống cả 2 component PostForm và PostList (data nhận được là postList cũng truyền xuống component PostList dưới dạng props) --> đây là flow hoạt động của component trong React, khi 2 component cần giao tiếp với nhau, thì chúng giao tiếp thông qua Component chung (component cha hoặc lớn hơn cha như ông, cụ,... :v )
- khi app lớn lên việc quản lý sẽ khó hơn nên bạn sẽ nên biết thêm các công cụ quản lý state như redux hay mobx hoặc dùng Context API của React (cái này thì khó dùng hơn 2 thằng kia nhưng ko cần cài thêm lib), cho phép bạn gọi trực tiếp function mà không cần truyền props qua nhiều lớp component. Anw, với bài toán hiện tại của bạn thì cứ làm như trên để hiểu đã nhé.
Vâng cám ơn bạn, cách thứ nhất bạn nói là cái đầu tiên mình nghĩ đến nhưng vì trong dự án thực tế, component
chung của của mình xử lý nhiều quá, nên mình muốn tách bớt phần xử lý này sang component
con để code clear hơn.
Thế nên m mới muốn hỏi xem có cách nào vẹn cả đôi đường không
bạn đang muốn reuse lại hàm getPostList
nhỉ, có vài cách tùy nhu cầu thực tế?
- chuyển hết logic của nó vào 1 cái saga, 2 component kia chỉ dispatch action (thông dụng nhất nhưng phải lắp lắm toy: redux, redux-saga)
- chuyển logic vào 1 hàm đặt ở component cha (đơn giản nhất nhưng 2 component sẽ bị ràng buộc về quan hệ)
- viết custom hook (chỉ áp dụng cho functional component)
- viết HOC (ngày xưa chưa có hook thì người ta hay dùng HOC)
nhưng trong ví dụ của bạn thì dùng cách nào cũng sida cả, vì vấn đề chính là: state của bạn đang để ở local component => bạn nên quay lại fix design như a @cuong_nguyen đề xuất thay vì quấn theo chiều gió :v, tức:
- gói 2 thằng vào component cha
- đẩy state và logic
getPostList
lên component cha - viết custom hook để lược bỏ logic của
getPostList
khỏi component cha
=> vừa đơn giản, vừa gọn, vẹn đôi đường (t hiểu đây là dự án chơi chơi thôi nhé chứ thực tế phải lắp redux vào r xài cách 1)
Theo mình hiểu thì ý tưởng của bạn là sau khi tạo mới sẽ gọi lại hàm getPostList để đẩy bài viết đó vào state, hiện ra view.
Để làm được điều này thì có 2 cách:
-
Bạn có thể tìm hiểu thêm về Redux Saga, ở đây chúng ta sẽ có một store lưu trữ dữ liệu chung, mỗi khi bạn postForm mới xong, bạn đẩy post đó vào store, ở phía postList bạn chỉ cần sử dụng các post từ store này, mỗi khi có post mới được đẩy vào store thì bên postList sẽ tự động hiển thị
-
Nếu bạn không biết về Redux Saga thì có thể tạo ra một Component chung cho 2 component PostForm và PostList, hàm getPostList bạn đặt trong component cha này. Sau đó bạn truyền hàm này vào component PostForm dưới dạng tham số, cứ mỗi khi có post mới được cập nhật bằng PostForm, bạn gọi đến getPostList.
Ở bên PostList, bạn truyền cho nó tham số post được lấy từ state của Component chung
// Component chung
state = {
posts: ""
};
componentDidMount() {
this.getPostList();
}
getPostList = () => {
const response = fetch("GET", "blog/post-list");
if (response.stattus === 200) {
this.setState({
posts: response.data
});
}
};
render {
const { posts } = this.state;
return ( <div>
<PostForm getPostList={this.getPostList} />
<PostList posts={posts} />
</div> )
}
Hàm call api getPostList
bắt buộc phải đặt trong component
chung, để có thể truyền hàm này thành pops
xuống cho comp createPost
để nó có thể gọi lại được đúng không bạn. Vẫn phải làm vậy thôi chứ ko có cách khác đúng ko ạ
@thanh_tuan bởi vì bạn không sử dụng redux store mà đang dùng local state nên bạn cần đặt hàm getPostList ở component chung để hàm này sau khi nhận data từ API có thể set được state trên component chung này
@hongquanfit nếu mình dùng Redux
thì có cách nào làm ngắn gọn không ạ, có phải xử lý trong component chung nữa ko bạn.
Bạn support thêm m dduwockj ko ?
Mình nghĩ dùng Redux
chỉ giúp m share được dữ liệu trong store
giữa các component thôi chứ ở đây mình đang cần share function
giữa các component mà phải không bạn ?
@thanh_tuan vấn đề là thế này bạn ơi. Khi bạn lưu dữ liệu tập trung, thì bạn có thể truy cập và thay đổi dữ liệu đó ở bất cứ component nào, tức là function getPostList kia của bạn có đặt ở component nào thì cũng đều có thể cập nhật lại list Post và tương tự, mọi component đều có thể lấy ra list Post, chứ nếu dùng local state, bạn lưu Post ở component nào thì chỉ có thể đặt function trong component đó thôi
Còn cách share thì như ở trên kìa bạn :v không phải tự dưng ng ta làm ra Redux cho vui thôi đâu
@hongquanfit thank b !
Bạn thử tìm hiểu về redux xem sao. Nó sẽ có 1 store để lưu dữ liệu và tất cả các component của bạn đều có thể lấy được dữ liệu từ store đó
cams ơn bạn, mình nghĩ có dùng redux
đi chăng nữa thì cũng vẫn phải gọi lại API getPostList
để cập nhật list post chứ nhỉ.
Hay nó có cách giải quyết hay hơn với bài toán này của m
@thanh_tuan đúng bạn, mình sẽ gọi lại cái API lấy ra list posts. Theo mình đọc code bạn share thì function getListPost
của bạn cũng đang là việc bạn gọi cái API đó mà
thank b !