Sử dụng children
const Wrapper = ({children}) => (
<div>
<div>header</div>
<div>{children}</div>
<div>footer</div>
</div>
);
const App = ({name}) => <div>Hello {name}</div>;
const WrappedApp = ({name}) => (
<Wrapper>
<App name={name}/>
</Wrapper>
);
render(<WrappedApp name="toto"/>,node);
Điều này cũng được gọi là transclusion
trong Angular.
children
là một chỗ dựa đặc biệt trong React và sẽ chứa những gì bên trong các thẻ thành phần của bạn (đây <App name={name}/>
là bên trong Wrapper
, vì vậy nó làchildren
Lưu ý rằng bạn không nhất thiết phải sử dụng children
, là duy nhất cho một thành phần và bạn cũng có thể sử dụng đạo cụ bình thường nếu bạn muốn, hoặc trộn đạo cụ và trẻ em:
const AppLayout = ({header,footer,children}) => (
<div className="app">
<div className="header">{header}</div>
<div className="body">{children}</div>
<div className="footer">{footer}</div>
</div>
);
const appElement = (
<AppLayout
header={<div>header</div>}
footer={<div>footer</div>}
>
<div>body</div>
</AppLayout>
);
render(appElement,node);
Điều này đơn giản và tốt cho nhiều usecase và tôi khuyên bạn nên dùng nó cho hầu hết các ứng dụng dành cho người tiêu dùng.
đạo cụ kết xuất
Có thể truyền các hàm kết xuất cho một thành phần, mẫu này thường được gọi render prop
và children
prop thường được sử dụng để cung cấp hàm gọi lại đó.
Mẫu này không thực sự có ý nghĩa cho bố cục. Thành phần trình bao bọc thường được sử dụng để giữ và quản lý một số trạng thái và đưa nó vào các chức năng kết xuất của nó.
Ví dụ truy cập:
const Counter = () => (
<State initial={0}>
{(val, set) => (
<div onClick={() => set(val + 1)}>
clicked {val} times
</div>
)}
</State>
);
Bạn có thể thậm chí còn lạ mắt hơn và thậm chí cung cấp một đối tượng
<Promise promise={somePromise}>
{{
loading: () => <div>...</div>,
success: (data) => <div>{data.something}</div>,
error: (e) => <div>{e.message}</div>,
}}
</Promise>
Lưu ý bạn không nhất thiết phải sử dụng children
, đó là vấn đề về hương vị / API.
<Promise
promise={somePromise}
renderLoading={() => <div>...</div>}
renderSuccess={(data) => <div>{data.something}</div>}
renderError={(e) => <div>{e.message}</div>}
/>
Cho đến ngày nay, nhiều thư viện đang sử dụng các đạo cụ kết xuất (bối cảnh React, React-motion, Apollo ...) vì mọi người có xu hướng tìm thấy API này dễ dàng hơn HOC. Reac-powerplug là một tập hợp các thành phần render-prop đơn giản. phản ứng-thông qua giúp bạn làm thành phần.
Các thành phần bậc cao (HOC).
const wrapHOC = (WrappedComponent) => {
class Wrapper extends React.PureComponent {
render() {
return (
<div>
<div>header</div>
<div><WrappedComponent {...this.props}/></div>
<div>footer</div>
</div>
);
}
}
return Wrapper;
}
const App = ({name}) => <div>Hello {name}</div>;
const WrappedApp = wrapHOC(App);
render(<WrappedApp name="toto"/>,node);
Một bậc cao Component / HOC thường là một chức năng mà phải mất một phần và trả về một thành phần mới.
Sử dụng Thành phần bậc cao có thể hiệu quả hơn so với sử dụng children
hoặc render props
, bởi vì trình bao bọc có thể có khả năng đoản mạch kết xuất trước một bước shouldComponentUpdate
.
Ở đây chúng tôi đang sử dụng PureComponent
. Khi kết xuất lại ứng dụng, nếu WrappedApp
tên prop không thay đổi theo thời gian, trình bao bọc có khả năng nói "Tôi không cần kết xuất vì đạo cụ (thực ra, tên) vẫn giống như trước". Với children
giải pháp dựa trên, ngay cả khi trình bao bọc PureComponent
không phải là trường hợp vì phần tử con được tạo lại mỗi khi cha mẹ kết xuất, điều đó có nghĩa là trình bao bọc có thể sẽ luôn hiển thị lại, ngay cả khi thành phần được bao bọc hoàn toàn. Có một plugin babel có thể giúp giảm thiểu điều này và đảm bảo một children
yếu tố không đổi theo thời gian.
Phần kết luận
Các thành phần bậc cao hơn có thể cung cấp cho bạn hiệu suất tốt hơn. Nó không quá phức tạp nhưng ban đầu chắc chắn trông không thân thiện.
Đừng di chuyển toàn bộ cơ sở mã của bạn sang HOC sau khi đọc nó. Chỉ cần nhớ rằng trên các đường dẫn quan trọng của ứng dụng, bạn có thể muốn sử dụng HOC thay vì trình bao bọc thời gian chạy vì lý do hiệu suất, đặc biệt nếu cùng một trình bao bọc được sử dụng nhiều lần thì nên xem xét biến nó thành HOC.
Redux ban đầu được sử dụng một trình bao bọc thời gian chạy <Connect>
và sau đó chuyển sang HOC connect(options)(Comp)
vì lý do hiệu suất (theo mặc định, trình bao bọc là thuần túy và sử dụng shouldComponentUpdate
). Đây là minh họa hoàn hảo cho những gì tôi muốn làm nổi bật trong câu trả lời này.
Lưu ý nếu một thành phần có API kết xuất, bạn thường dễ dàng tạo HOC trên đầu trang, vì vậy nếu bạn là tác giả lib, trước tiên bạn nên viết API kết xuất và cuối cùng cung cấp phiên bản HOC. Đây là những gì Apollo làm với <Query>
thành phần render-prop và graphql
HOC sử dụng nó.
Cá nhân, tôi sử dụng cả hai, nhưng khi nghi ngờ tôi thích HOC hơn vì:
- Thật đơn giản hơn khi soạn chúng (
compose(hoc1,hoc2)(Comp)
) so với đạo cụ kết xuất
- Nó có thể cho tôi những màn trình diễn tốt hơn
- Tôi quen thuộc với phong cách lập trình này
Tôi không ngần ngại sử dụng / tạo các phiên bản HOC của các công cụ yêu thích của mình:
- Phản ứng của
Context.Consumer
comp
- Không nói
Subscribe
- sử dụng
graphql
HOC của Apollo thay vì Query
kết xuất prop
Theo tôi, đôi khi các đạo cụ kết xuất làm cho mã dễ đọc hơn, đôi khi ít hơn ... Tôi cố gắng sử dụng giải pháp thực dụng nhất theo các ràng buộc mà tôi có. Đôi khi khả năng đọc quan trọng hơn màn trình diễn, đôi khi không. Chọn một cách khôn ngoan và không ràng buộc theo xu hướng năm 2018 là chuyển đổi mọi thứ thành đạo cụ kết xuất.