web-dev-qa-db-ja.com

列が文字列ではなくJSON配列である場合にANTDテーブルでフィルタリングする方法

2つの列を持つantdテーブルがあり、最初の列でフィルタリングし、2番目の列でテキストを検索する必要があります。

私のコードから、アプリケーションは正常にレンダリングされます。 tagsフィールドはテキストフィールドではなくjson配列であることに注意してください。そのため、エラーと関係があると思います。

1コードを更新。

_import React, { Component } from 'react';
import {  Table, Tag, Button, Icon, Input} from 'antd';
import { adalApiFetch } from '../../adalConfig';
import Notification from '../../components/notification';
import Highlighter from 'react-highlight-words';

class ListPageTemplatesWithSelection extends Component {

    constructor(props) {
        super(props);
        this.state = {
            data: [],
            filteredInfo: null,
            sortedInfo: null,
            searchText: ''
        };
        this.handleChange= this.handleChange.bind(this);
        this.clearFilters= this.clearFilters.bind(this);
        this.clearAll= this.clearAll.bind(this);
        this.getColumnSearchProps= this.getColumnSearchProps.bind(this);
        this.handleSearch= this.handleSearch.bind(this);
        this.handleReset= this.handleReset.bind(this);

    }

    handleSearch (selectedKeys, confirm){
      confirm();
      this.setState({ searchText: selectedKeys[0] });
    }

    handleReset(clearFilters){
      clearFilters();
      this.setState({ searchText: '' });
    }

    getColumnSearchProps = (dataIndex) => ({
        filterDropdown: ({
        setSelectedKeys, selectedKeys, confirm, clearFilters,
      }) => (
        <div style={{ padding: 8 }}>
          <Input
            ref={node => { this.searchInput = node; }}
            placeholder={`Search ${dataIndex}`}
            value={selectedKeys[0]}
            onChange={e => setSelectedKeys(e.target.value ? [e.target.value] : [])}
            onPressEnter={() => this.handleSearch(selectedKeys, confirm)}
            style={{ width: 188, marginBottom: 8, display: 'block' }}
          />
          <Button
            type="primary"
            onClick={() => this.handleSearch(selectedKeys, confirm)}
            icon="search"
            size="small"
            style={{ width: 90, marginRight: 8 }}
          >
            Search
          </Button>
          <Button
            onClick={() => this.handleReset(clearFilters)}
            size="small"
            style={{ width: 90 }}
          >
            Reset
          </Button>
        </div>
      ),
      filterIcon: filtered => <Icon type="search" style={{ color: filtered ? '#1890ff' : undefined }} />,
      onFilter: (value, record) =>
      record[dataIndex]
        ? record[dataIndex]
            .toString()
            .toLowerCase()
            .includes(value.toLowerCase())
        : false,
      onFilterDropdownVisibleChange: (visible) => {
        if (visible) {
          setTimeout(() => this.searchInput.select());
        }
      }
    })

    handleChange(pagination, filters, sorter){
      console.log('Various parameters', pagination, filters, sorter);
      this.setState({
        filteredInfo: filters,
        sortedInfo: sorter,
      });
    }

    clearFilters(){
      this.setState({ filteredInfo: null });
    }

    clearAll(){
      this.setState({
        filteredInfo: null,
        sortedInfo: null,
      });
    }

    fetchData = () => {
        adalApiFetch(fetch, "/PageTemplates", {})
          .then(response => response.json())
          .then(responseJson => {
            if (!this.isCancelled) {
                const results= responseJson.map(row => ({
                    key: row.Id,
                    Name: row.Name,
                    SiteType: row.SiteType,
                    Tags: row.Tags
                  }))
              this.setState({ data: results });
            }
          })
          .catch(error => {
            console.error(error);
          });
      };


    componentDidMount(){
        this.fetchData();
    }

    render(){
          let { sortedInfo, filteredInfo } = this.state;
        sortedInfo = sortedInfo || {};
        filteredInfo = filteredInfo || {};

        const columns = [
                {
                    title: 'Id',
                    dataIndex: 'key',
                    key: 'key',
                }, 
                {
                    title: 'Name',
                    dataIndex: 'Name',
                    key: 'Name',
                }, 
                {
                    title: 'Site Type',
                    dataIndex: 'SiteType',
                    key: 'SiteType',
                    filters: [
                      { text: 'Modern Team Site', value: 'Modern Team Site' },
                      { text: 'CommunicationSite', value: 'CommunicationSite' },
                    ],
                    filteredValue: filteredInfo.SiteType || null,
                    onFilter: (value, record) => record.SiteType.includes(value),
                },{
                  title: 'Tags',
                  key: 'Tags',
                  dataIndex: 'Tags',
                  ...this.getColumnSearchProps('Tags'),
                  render: Tags => (
                    <span>
                    {Tags && Tags.map(tag => {
                      let color = tag.length > 5 ? 'geekblue' : 'green';
                      if (tag === 'loser') {
                        color = 'volcano';
                      }
                      return <Tag color={color} key={tag}>{tag.toUpperCase()}</Tag>;
                    })}
                  </span>)

                }
        ];

        const rowSelection = {
            selectedRowKeys: this.props.selectedRows,
            onChange: (selectedRowKeys) => {
              this.props.onRowSelect(selectedRowKeys);
            }
          };


        return (
          <div>
            <Button onClick={this.clearFilters}>Clear filters</Button>
            <Button onClick={this.clearAll}>Clear filters and sorters</Button>
            <Table rowSelection={rowSelection}  columns={columns} dataSource={this.state.data} onChange={this.handleChange} />
          </div>
        );
    }
}

export default ListPageTemplatesWithSelection;
_

しかし、この行を追加すると:...this.getColumnSearchProps('Tags'),

次に、このエラーが発生します

_Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.
▶ 23 stack frames were collapsed.
AsyncFunc._callee$
src/helpers/AsyncFunc.js:26
  23 | const { default: Component } = await importComponent();
  24 | Nprogress.done();
  25 | if (this.mounted) {
> 26 |   this.setState({
  27 |     component: <Component {...this.props} />
  28 |   });
  29 | }
_

アップデート2

これはコンテナコンポーネントです

_import React, { Component } from 'react';
import { Input} from 'antd';
import Form from '../../components/uielements/form';
import Button from '../../components/uielements/button';
import Notification from '../../components/notification';
import { adalApiFetch } from '../../adalConfig';
import   ListPageTemplatesWithSelection  from './ListPageTemplatesWithSelection';

const FormItem = Form.Item;

class CreateCommunicationSiteCollectionForm extends Component {
    constructor(props) {
        super(props);
        this.state = {Title:'',Url:'', SiteDesign:'', Description:'',Owner:'',Lcid:'', PageTemplateIds : []};
        this.handleChangeTitle = this.handleChangeTitle.bind(this);
        this.handleValidationCommunicationSiteUrl = this.handleValidationCommunicationSiteUrl.bind(this);
        this.handleChangeCommunicationSiteUrl = this.handleChangeCommunicationSiteUrl.bind(this);
        this.handleChangeSiteDesign = this.handleChangeSiteDesign.bind(this);
        this.handleChangeDescription = this.handleChangeDescription.bind(this);
        this.handleChangeOwner = this.handleChangeOwner.bind(this);
        this.handleChangelcid = this.handleChangelcid.bind(this);

        this.handleSubmit = this.handleSubmit.bind(this);
        this.handleRowSelect = this.handleRowSelect.bind(this);
    }

    handleRowSelect(ids) {
        this.setState({ PageTemplateIds: ids });
    }

    handleChangeTitle(event){
        this.setState({Title: event.target.value});
    }

    handleValidationCommunicationSiteUrl(rule, value, callback){
        const form = this.props.form;
        const str = form.getFieldValue('communicationsiteurl');        
        var re = /^(?:http(s)?:\/\/)?[\w.-]+(?:\.[\w\.-]+)+[\w\-\._~:/?#[\]@!\$&'\(\)\*\+,;=.]+$/i;
        if (str && !str.match(re)) {
            callback('Communication site url is not correctly formated.');
        } 
        else {
            callback();
        }
    }

    handleChangeCommunicationSiteUrl(event){
        this.setState({Url: event.target.value});
    }

    handleChangeSiteDesign(event){
        this.setState({SiteDesign: event.target.value});
    }

    handleChangeDescription(event){
        this.setState({Description: event.target.value});
    }

    handleChangeOwner(event){
        this.setState({Owner: event.target.value});
    }

    handleChangelcid(event){
        this.setState({Lcid: event.target.value});
    }

    handleSubmit(e){
        e.preventDefault();
        this.props.form.validateFieldsAndScroll((err, values) => {
            if (!err) {
                let data = new FormData();
                //Append files to form data
                //data.append(

                const options = {
                  method: 'post',
                  body: JSON.stringify(
                    {
                        "Title": this.state.Title,
                        "Url": this.state.Url, 
                        "SiteDesign": this.state.SiteDesign,
                        "Description": this.state.Description,
                        "Owner": this.state.Owner,
                        "Lcid": this.state.Lcid,
                        "PageTemplateIds": this.state.PageTemplateIds
                    }),
                    headers: {
                            'Content-Type': 'application/json; charset=utf-8'
                    }                    
                };

                adalApiFetch(fetch, "/SiteCollection/CreateCommunicationSite", options)
                  .then(response =>{
                    if(response.status === 201){
                        Notification(
                            'success',
                            'Communication Site created',
                            ''
                            );
                     }else{
                        throw "error";
                     }
                  })
                  .catch(error => {
                    Notification(
                        'error',
                        'Site collection not created',
                        error
                        );
                    console.error(error);
                });
            }
        });      
    }

    render() {
        const { getFieldDecorator } = this.props.form;
        const formItemLayout = {
        labelCol: {
            xs: { span: 24 },
            sm: { span: 6 },
        },
        wrapperCol: {
            xs: { span: 24 },
            sm: { span: 14 },
        },
        };
        const tailFormItemLayout = {
        wrapperCol: {
            xs: {
            span: 24,
            offset: 0,
            },
            sm: {
            span: 14,
            offset: 6,
            },
        },
        };
        return (
            <Form onSubmit={this.handleSubmit}>
                <FormItem {...formItemLayout} label="Title" hasFeedback>
                {getFieldDecorator('Title', {
                    rules: [
                        {
                            required: true,
                            message: 'Please input your communication site title',
                        }
                    ]
                })(<Input name="title" id="title" onChange={this.handleChangeTitle} />)}
                </FormItem>
                <FormItem {...formItemLayout} label="Communication Site Url" hasFeedback>
                {getFieldDecorator('communicationSiteUrl', {
                    rules: [
                        {
                            required: true,
                            message: 'CommunicationSite site collection url',
                        },
                        {
                            validator: this.handleValidationCommunicationSiteUrl
                        }
                    ]
                })(<Input name="communicationsSiteUrl" id="communicationsSiteUrl" onChange={this.handleChangeCommunicationSiteUrl} />)}
                </FormItem>
                <FormItem {...formItemLayout} label="Site Design" hasFeedback>
                {getFieldDecorator('sitedesign', {
                    rules: [
                        {
                            required: true,
                            message: 'Please input your site design',
                        }
                    ]
                })(<Input name="sitedesign" id="sitedesign" onChange={this.handleChangeSiteDesign} />)}
                </FormItem>
                <FormItem {...formItemLayout} label="Description" hasFeedback>
                {getFieldDecorator('description', {
                    rules: [
                        {
                            required: true,
                            message: 'Please input your description',
                        }
                    ],
                })(<Input name="description" id="description"  onChange={this.handleChangeDescription} />)}
                </FormItem>
                <FormItem {...formItemLayout} label="Owner" hasFeedback>
                {getFieldDecorator('owner', {
                    rules: [
                        {
                            required: true,
                            message: 'Please input your owner',
                        }
                    ],
                })(<Input name="owner" id="owner"  onChange={this.handleChangeOwner} />)}
                </FormItem>
                <FormItem {...formItemLayout} label="Lcid" hasFeedback>
                {getFieldDecorator('lcid', {
                    rules: [
                        {
                            required: true,
                            message: 'Please input your lcid',
                        }
                    ],
                })(<Input name="lcid" id="lcid"  onChange={this.handleChangelcid} />)}
                </FormItem>          

                <ListPageTemplatesWithSelection onRowSelect={this.handleRowSelect} selectedRows={this.state.PageTemplateIds}/>


                <FormItem {...tailFormItemLayout}>
                    <Button type="primary" htmlType="submit">
                        Create communication site
                    </Button>
                </FormItem>


            </Form>



        );
    }
}

const WrappedCreateCommunicationSiteCollectionForm = Form.create()(CreateCommunicationSiteCollectionForm);
export default WrappedCreateCommunicationSiteCollectionForm;
_
18
Luis Valencia

あなたが提供したエラーから何が悪いのかを推測することは非常に困難です。だから私ができる最善のことは、あなたが注意すべきいくつかのことを指摘することです。

getColumnSearchProps()のrenderメソッドに誤りがあります。テキストがnullの場合(タグがない行のため)、テキストを文字列に変換しようとしてクラッシュします。これを回避するには、レンダリングの前にtextが存在することを確認します。

_render: text =>
      text ? (
        <Highlighter
          highlightStyle={{ backgroundColor: "#ffc069", padding: 0 }}
          searchWords={[this.state.searchText]}
          autoEscape
          textToHighlight={text.toString()}
        />
      ) : null
_

同じことがonFilterメソッドにも当てはまります。

_onFilter: (value, record) =>
  record[dataIndex]
    ? record[dataIndex]
        .toString()
        .toLowerCase()
        .includes(value.toLowerCase())
    : false,
_

タグ列をレンダリングするための2つのrenderメソッドがあります。 1つはgetColumnSearchProps()内にあり、もう1つは...this.getColumnSearchProps('Tags')呼び出しの後です。後者が前のものを上書きするので、これは問題ないはずです。もう一度、なぜあなたがそれを必要としないなら、あなたは貴重なものを宣言するのですか?

お役に立てれば。

9
mehamasum

別のスレッドで述べたように、存在しないコンポーネントをインポートしようとすると、このエラーが発生する可能性があります。タイプミスがないこと、およびコンポーネントが実際にそのように命名されていることを確認してください。ライブラリの場合は、コンポーネントが異なるバージョンで異なる名前を持つ可能性があるため、適切なバージョンを使用していることを確認してください。

同様の問題のあるリンク:
キャッチされないエラー:不変の違反:要素タイプが無効です:文字列(組み込みコンポーネントの場合)またはクラス/関数が必要ですが、取得されました:オブジェクト
React.createElement:タイプが無効です-文字列またはクラス/関数が必要ですが、取得されました:未定義

1
Fraction