web-dev-qa-db-ja.com

反応コンポーネントで特定のタイプの子のみを許可する

CardコンポーネントとCardGroupコンポーネントがあり、CardGroupCardコンポーネントではない子を持つ場合にエラーをスローしたいと思います。これは可能ですか、それとも間違った問題を解決しようとしていますか?

39
bigblind

タイプを介してアクセスされる各子のdisplayNameを使用できます。

for (child in this.props.children){
  if (this.props.children[child].type.displayName != 'Card'){
    console.log("Warning CardGroup has children that aren't Card components");
  }  
}
19
Mark

React 0.14+およびES6クラスを使用する場合、ソリューションは次のようになります。

class CardGroup extends Component {
  render() {
    return (
      <div>{this.props.children}</div>
    )
  }
}
CardGroup.propTypes = {
  children: function (props, propName, componentName) {
    const prop = props[propName]

    let error = null
    React.Children.forEach(prop, function (child) {
      if (child.type !== Card) {
        error = new Error('`' + componentName + '` children should be of type `Card`.');
      }
    })
    return error
  }
}
43
Diego V

子は単なる小道具なので、カスタムpropType関数を使用して子を検証できます。さらに詳細が必要な場合は、これについて 記事 も書いています。

var CardGroup = React.createClass({
  propTypes: {
    children: function (props, propName, componentName) {
      var error;
      var prop = props[propName];

      React.Children.forEach(prop, function (child) {
        if (child.type.displayName !== 'Card') {
          error = new Error(
            '`' + componentName + '` only accepts children of type `Card`.'
          );
        }
      });

      return error;
    }
  },

  render: function () {
    return (
      <div>{this.props.children}</div>
    );
  }
});
13
mzabriskie

equalToを呼び出すカスタムPropTypeを作成しました。このように使用できます...

class MyChildComponent extends React.Component { ... }

class MyParentComponent extends React.Component {
  static propTypes = {
    children: PropTypes.arrayOf(PropTypes.equalTo(MyChildComponent))
  }
}

現在、MyParentComponentMyChildComponentである子のみを受け入れます。このようなhtml要素を確認できます...

PropTypes.equalTo('h1')
PropTypes.equalTo('div')
PropTypes.equalTo('img')
...

これが実装です...

React.PropTypes.equalTo = function (component) {
  return function validate(propValue, key, componentName, location, propFullName) {
    const prop = propValue[key]
    if (prop.type !== component) {
      return new Error(
        'Invalid prop `' + propFullName + '` supplied to' +
        ' `' + componentName + '`. Validation failed.'
      );
    }
  };
}

これを簡単に拡張して、考えられる多くのタイプのいずれかを受け入れることができます。たぶん...

React.PropTypes.equalToOneOf = function (arrayOfAcceptedComponents) {
...
}
4
Charlie Martin
static propTypes = {

  children : (props, propName, componentName) => {
              const prop = props[propName];
              return React.Children
                       .toArray(prop)
                       .find(child => child.type !== Card) && new Error(`${componentName} only accepts "<Card />" elements`);
  },

}
4
Abdennour TOUMI

型の不一致エラーを回避するためにTypeScriptを使用している場合は、「child.type」とともに「React.isValidElement(child)」を使用する必要があります。

React.Children.forEach(props.children, (child, index) => {
  if (React.isValidElement(child) && child.type !== Card) {
    error = new Error(
      '`' + componentName + '` only accepts children of type `Card`.'
    );
  }
});
3
Karna

正しい子コンポーネントを検証するには、 react children foreachカスタム検証proptypes の使用を組み合わせるため、最後に次のようになります。

HouseComponent.propTypes = {
children: PropTypes.oneOfType([(props, propName, componentName) => {
    let error = null;
    const validInputs = [
    'Mother',
    'Girlfried',
    'Friends',
    'Dogs'
    ];
    // Validate the valid inputs components allowed.
    React.Children.forEach(props[propName], (child) => {
            if (!validInputs.includes(child.type.name)) {
                error = new Error(componentName.concat(
                ' children should be one of the type:'
                    .concat(validInputs.toString())
            ));
        }
    });
    return error;
    }]).isRequired
};

ご覧のとおり、正しい型の名前を持つ配列があります。

一方、airbnb/prop-typesライブラリには、同じ結果を得るのに役立つcomponentWithNameという関数もあります。 ここで詳細を確認できます

HouseComponent.propTypes = {
    children: PropTypes.oneOfType([
        componentWithName('SegmentedControl'),
        componentWithName('FormText'),
        componentWithName('FormTextarea'),
        componentWithName('FormSelect')
    ]).isRequired
};

これが何らかの助けになることを願っています:)

2
Ismael Terreno

React.Children.forEachメソッドを使用して子を反復処理し、nameプロパティを使用して型を確認します。

React.Children.forEach(this.props.children, (child) => {
    if (child.type.name !== Card.name) {
        console.error("Only card components allowed as children.");
    }
}

uglifyに関するメンテナンスと安定性を向上させるために、Card.name文字列の代わりに'Card'文字列を使用することをお勧めします。

参照: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/name

2
Salim

Cardコンポーネントに小道具を追加し、CardGroupコンポーネントでこの小道具を確認できます。これは、Reactでこれを達成する最も安全な方法です。

このプロップはdefaultPropとして追加できるため、常にそこにあります。

class Card extends Component {

  static defaultProps = {
    isCard: true,
  }

  render() {
    return (
      <div>A Card</div>
    )
  }
}

class CardGroup extends Component {

  render() {
    for (child in this.props.children) {
      if (!this.props.children[child].props.isCard){
        console.error("Warning CardGroup has a child which isn't a Card component");
      }
    }

    return (
      <div>{this.props.children}</div>
    )
  }
}

typeまたはdisplayNameを使用してCardコンポーネントが実際にCardコンポーネントであるかどうかを確認することは安全ではありません。以下に示すように、実稼働使用中に動作しない可能性があります。 https:// github .com/facebook/react/issues/6167#issuecomment-191243709

2
Hedley Smith

私のような人には、TypeScriptバージョンを使用します。次のようにコンポーネントをフィルタリング/変更できます。

this.modifiedChildren = React.Children.map(children, child => {
            if (React.isValidElement(child) && (child as React.ReactElement<any>).type === Card) {
                let modifiedChild = child as React.ReactElement<any>;
                // Modifying here
                return modifiedChild;
            }
            // Returning other components / string.
            // Delete next line in case you dont need them.
            return child;
        });
2
SLCH000

React elements https://www.npmjs.com/package/react-element-proptypes のタイプを検証できるパッケージを公開しました。

const ElementPropTypes = require('react-element-proptypes');

const Modal = ({ header, items }) => (
    <div>
        <div>{header}</div>
        <div>{items}</div>
    </div>
);

Modal.propTypes = {
    header: ElementPropTypes.elementOfType(Header).isRequired,
    items: React.PropTypes.arrayOf(ElementPropTypes.elementOfType(Item))
};

// render Modal 
React.render(
    <Modal
       header={<Header title="This is modal" />}
       items={[
           <Item/>,
           <Item/>,
           <Item/>
       ]}
    />,
    rootElement
);
1
wizardzloy

私にとってこれを達成する最も簡単な方法は、次のコードです。

例1:

import React, {Children} from 'react';

function myComponent({children}) {

  return (
    <div>{children && Children.map(children, child => {
      if (child.type === 'div') return child
    })}</div>
  )
}

export default myComponent;

例2-コンポーネントあり

import React, {Children} from 'react';

function myComponent({children}) {

  return (
    <div>{children && Children.map(children, child => {
      if (child.type.displayName === 'Card') return child
    })}</div>
  )
}

export default myComponent;
0
Ballpin