Làm cách nào để tôi bọc một thành phần React có điều kiện?


85

Tôi có một thành phần đôi khi sẽ cần được hiển thị dưới dạng một <anchor>và những lần khác dưới dạng a <div>. Các proptôi đọc để xác định điều này, là this.props.url.

Nếu nó tồn tại, tôi cần kết xuất thành phần được bao bọc trong một <a href={this.props.url}>. Nếu không, nó chỉ được hiển thị dưới dạng <div/>.

Khả thi?

Đây là những gì tôi đang làm ngay bây giờ, nhưng cảm thấy nó có thể được đơn giản hóa:

if (this.props.link) {
    return (
        <a href={this.props.link}>
            <i>
                {this.props.count}
            </i>
        </a>
    );
}

return (
    <i className={styles.Icon}>
        {this.props.count}
    </i>
);

CẬP NHẬT:

Đây là khóa cuối cùng. Cảm ơn vì mẹo, @Sulthan !

import React, { Component, PropTypes } from 'react';
import classNames from 'classnames';

export default class CommentCount extends Component {

    static propTypes = {
        count: PropTypes.number.isRequired,
        link: PropTypes.string,
        className: PropTypes.string
    }

    render() {
        const styles = require('./CommentCount.css');
        const {link, className, count} = this.props;

        const iconClasses = classNames({
            [styles.Icon]: true,
            [className]: !link && className
        });

        const Icon = (
            <i className={iconClasses}>
                {count}
            </i>
        );

        if (link) {
            const baseClasses = classNames({
                [styles.Base]: true,
                [className]: className
            });

            return (
                <a href={link} className={baseClasses}>
                    {Icon}
                </a>
            );
        }

        return Icon;
    }
}

Bạn cũng có thể chuyển const baseClasses =vào if (this.props.link)chi nhánh đó . Khi bạn đang sử dụng ES6, vì vậy bạn cũng có thể đơn giản hóa một chút bằng const {link, className} = this.props;cách sử dụng linkclassNamedưới dạng các biến cục bộ.
Sulthan

Người đàn ông, tôi yêu nó. Tìm hiểu ngày càng nhiều về ES6 và nó luôn cải thiện khả năng đọc. Cảm ơn cho mẹo bổ sung!
Brandon Durham

1
"Khóa cuối cùng" là gì?
Chris Harrison

Câu trả lời:


92

Chỉ cần sử dụng một biến.

var component = (
    <i className={styles.Icon}>
       {this.props.count}
    </i>
);

if (this.props.link) {
    return (
        <a href={this.props.link} className={baseClasses}>
            {component}
        </a>
    );
}

return component;

hoặc, bạn có thể sử dụng chức năng trợ giúp để hiển thị nội dung. JSX là mã giống như bất kỳ mã nào khác. Nếu bạn muốn giảm bớt sự trùng lặp, hãy sử dụng các hàm và biến.


21

Tạo một HOC (thành phần bậc cao hơn) để gói phần tử của bạn:

const WithLink = ({ link, className, children }) => (link ?
  <a href={link} className={className}>
    {children}
  </a>
  : children
);

return (
  <WithLink link={this.props.link} className={baseClasses}>
    <i className={styles.Icon}>
      {this.props.count}
    </i>
  </WithLink>
);

4
HOC nên chết dần chết
mòn

Thuật ngữ HOCnày thật gớm ghiếc. Nó chỉ đơn thuần là một chức năng được đặt ở giữa. Tôi thực sự thay thế cái tên bất ngờ thời thượng này "HPC". thứ quá cao về một chức năng đơn giản nằm giữa ... khái niệm cũ trong nhiều thập kỷ.
vsync

12

Dưới đây là một ví dụ về một thành phần hữu ích mà tôi đã thấy được sử dụng (không chắc ai sẽ công nhận nó cho) thực hiện công việc:

const ConditionalWrap = ({ condition, wrap, children }) => (
  condition ? wrap(children) : children
);

Trường hợp sử dụng:

<ConditionalWrap condition={someCondition}
  wrap={children => (<a>{children}</a>)} // Can be anything
>
  This text is passed as the children arg to the wrap prop
</ConditionalWrap>

2
Tín dụng có thể sẽ xuất hiện ở đây: gist.github.com/kitze/23d82bb9eb0baabfd03a6a720b1d637f
Roy Prins

Tôi đã thấy điều đó từ kitze. Nhưng tôi không chắc anh ấy lấy ý tưởng từ người khác
antony

Tôi cũng vậy. Đây là kết quả đầu tiên xuất hiện và tôi cho rằng nó là nguồn gốc - hoặc ít nhất là gần nó hơn;).
Roy Prins

Bạn nên sử dụng wrapkhai báo chứ không phải như một hàm để giữ cho mọi thứ "React" hơn -spirit
vsync

Làm thế nào bạn sẽ làm cho nó @vsync khai báo hơn? Tôi nghĩ các đạo cụ kết xuất nằm trong tinh thần của React?
antony

10

Có một cách khác bạn có thể sử dụng biến tham chiếu

let Wrapper = React.Fragment //fallback in case you dont want to wrap your components

if(someCondition) {
    Wrapper = ParentComponent
}

return (
    <Wrapper parentProps={parentProps}>
        <Child></Child>
    </Wrapper>

)

Bạn có thể cô đọng hiệp một thànhlet Wrapper = someCondition ? ParentComponent : React.Fragment
mpoisot

Điều này thật tuyệt vời, nhưng đôi khi bạn muốn giữ mã khai báo , nghĩa là nó chỉ trả về JSX
vsync

Tôi gặp lỗi React.Fragment can only have 'key' and 'children' vì chuyển một số đạo cụ cho "<Wrapper>" như "className" , v.v.
vsync

@vsync, bạn cũng cần thêm một điều kiện cho các đạo cụ như propId = {someCondition? parentProps: undefined} ..
Avinash

1
Tôi biết :) Tôi viết bài này vì mục đích cung cấp tài liệu cho những người khác đến đây với vấn đề này, vì vậy Google sẽ lưu trang này vào bộ nhớ cache trong kết quả tìm kiếm cho những từ khóa đó
vsync

1

Bạn cũng có thể sử dụng một hàm sử dụng như thế này:

const wrapIf = (conditions, content, wrapper) => conditions
        ? React.cloneElement(wrapper, {}, content)
        : content;

0

Bạn nên sử dụng JSX if-else như được mô tả ở đây . Một cái gì đó như thế này sẽ hoạt động.

App = React.creatClass({
    render() {
        var myComponent;
        if(typeof(this.props.url) != 'undefined') {
            myComponent = <myLink url=this.props.url>;
        }
        else {
            myComponent = <myDiv>;
        }
        return (
            <div>
                {myComponent}
            </div>
        )
    }
});

-2

Một thành phần chức năng hiển thị 2 thành phần, một thành phần được bao bọc và thành phần còn lại thì không.

Phương pháp 1:

// The interesting part:
const WrapIf = ({ condition, With, children, ...rest }) => 
  condition 
    ? <With {...rest}>{children}</With> 
    : children

 
    
const Wrapper = ({children, ...rest}) => <h1 {...rest}>{children}</h1>


// demo app: with & without a wrapper
const App = () => [
  <WrapIf condition={true} With={Wrapper} style={{color:"red"}}>
    foo
  </WrapIf>
  ,
  <WrapIf condition={false} With={Wrapper}>
    bar
  </WrapIf>
]

ReactDOM.render(<App/>, document.body)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

Điều này cũng có thể được sử dụng như thế này:

<WrapIf condition={true} With={"h1"}>

Phương pháp 2:

// The interesting part:
const Wrapper = ({ condition, children, ...props }) => condition 
  ? <h1 {...props}>{children}</h1>
  : <React.Fragment>{children}</React.Fragment>;   
    // stackoverflow prevents using <></>
  

// demo app: with & without a wrapper
const App = () => [
  <Wrapper condition={true} style={{color:"red"}}>
    foo
  </Wrapper>
  ,
  <Wrapper condition={false}>
    bar
  </Wrapper>
]

ReactDOM.render(<App/>, document.body)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.