React Ant Designツリーの複雑な添削操作


最近は業務のためにANtdに接触しました。ANtdを使って複雑なツリーの表示と修正を完成しました。この中で多くのピットに出会いました。多くの機能antdは初歩的な機能だけを書いています。さらに細かい機能は自分で完璧にするしかないです。踏んだ穴は全部ここに書いてあります。
ツリーテーブルの表示
antdでは表のkey値に対して厳格なコントロールがあります。各rowにはユニークなkey値が必要です。数字でも文字列でもいいです。この点は私が使ったiviewと大きな違いがあります。reactはkeyを使って各行を表現するのは、再レンダリングの問題を避けるためであり、この最適化も実際の開発に多くの問題をもたらしている。たとえば、新しい行を作成するには、新しいキーが必要です。
下に直接コードとコードの効果をつけます。これは三段階のツリーテーブルで、二級のタイトルが含まれています。
最終効果

columsのタイトル:簡易版のタイトルは、機能の増加に伴って、columsの複雑さを増加させます。

let columns = [
 {
  title: '  ',
  dataIndex: 'text'
 },
 {
  title: '  ',
  children: [
   {
    title: '  ',
    dataIndex: 'text1'
   },
   {
    title: '  ',
    dataIndex: 'text2',
   }]
 },
 {
  title: '  ',
  dataIndex: 'content'
 },
 {
  title: '  ',
  dataIndex: 'answer',
 },
 {
  title: '  ',
  dataIndex: 'mark_type',
  className: 'line'
 },
 {
  title: '  ',
  dataIndex: 'version',
  className: 'line'
 },
 {
  title: '     ',
  dataIndex: 'value1',
  className: 'line'
 },
 {
  title: '     ',
  dataIndex: 'value2',
  className: 'line'
 },
 {
  title: '  ',
  key: 'action',
  width: 205
 }
];

データ:

let data = [{
   "key": 1,
   "text": "   ",
   "children": [{
    "key": 11,
    "text1": "   ",
    "children":[]
   }, {
    "key": 12,
    "text1": "   ",
    "value1": "  ",
    "children": [{
     "key": 121,
     "value2": "  ",
     "text2": "   ",
     "content": "    ",
     "answer": "A",
     "mark_type": "1",
     "version": "1"
    },{
     "key": 122,
     "value2":"  ",
     "text2": "   ",
     "content": "    ",
     "answer": "  ",
     "mark_type": "1",
     "version": "1"
    },{
     "key": 123,
     "value2": "  ",
     "text2": "   ",
     "content": "    ",
     "answer": "    ",
     "mark_type": "1",
     "version": "1"
    },{
     "key": 124,
     "value2": "   ",
     "text2": "   ",
     "content": "    ",
     "answer": "    ",
     "mark_type": "1",
     "version": "1"
    }],
   }],
  }, {
   "key": 2,
   "text": "   ",
   "children": [ {
    "key": 21,
    "text1": "   ",
    "value1": "  ",
    "children": [{
     "key": 211,
     "value2": "  ",
     "text2": "    ",
     "content": "    ",
     "answer": "ABC",
     "mark_type": "2",
     "version": "1"
    },{
     "key": 212,
     "value2": "    ",
     "text2": "  ",
     "content": "    ",
     "answer": "D",
     "mark_type": "2",
     "version": "1"
    }],
   }],
  }];
サブデータを追加
サブデータを追加して操作中の追加ボタンを使って増加し、追加ボタンを図に設定し、より具体的にイメージをクリアします。

 //button    antd  icon  
<Button type="primary" shape="circle" icon="plus" size={'small'}
   onClick={this.handleAdd.bind(this, record)}
 />
注意事項:
1、サブ項目に対して、サブ項目の選択を追加していません。異なるデータ行に対して異なる処理が必要です。
2、データ行によってデータが追加される内容が異なり、フィールドも異なります。
3、追加後はクリックして確認またはキャンセルする必要があります。
4、加入データのクリック確認後、現在のkeyのchildrenに追加する必要があります。
ANtdが持つエディットable属性を使いたいですが、この属性は二級タイトルの編集には対応していませんので、自分でレンダーを書くしかありません。
この問題は7102年に提起されましたが、もう8102年になりました。9102年近く更新されていません。
レベル1のテーマスタイルを追加します。

二級テーマのスタイルを追加します。

ここで一部のレンダーコードについてです。

//    
{
 title: '  ', 
 children: 
 [{
  title: '  ',
  dataIndex: 'text1',
  render: (text, record) => {
   if (this.state.isEditing && this.state.editingOneKey === record.key)
    return <Input defaultValue={text} onChange={(e) => { this.changeEdit(e, record.key, 'text1') }} />
   return text
  }
 },
 {
  title: '  ',
  dataIndex: 'text2',
  render: (text, record) => {
   if (this.state.isEditing && this.state.editingTwoKey === record.key)
    return <Input defaultValue={text} onChange={(e) => { this.changeEdit(e, record.key, 'text2') }} />
   return text
  }
 }]
}

//select  
{
 title: '  ',
 dataIndex:'content',
 render:(text, record) => {
  if (text === 0)
   text = '   ';
  if (text === 1)
   text = '   ';
  if (this.state.isEditing && this.state.editingTwoKey === record.key) {
   return <Select defaultValue={text} onChange={(e) => {
    this.changeEdit(e, record.key, 'flag')
   }} getPopupContainer={triggerNode => triggerNode.parentNode}>
    <Option value='0'>  </Option>
    <Option value='1'>  </Option>
   </Select>
  }
  return text
 }
}

展開に注意する
行が展開されていない行にデータが追加されると、開いている編集状態が見えなくなります。非常に悪い問題です。だから、私たちはサブアイテムを追加する時に、自動的に親項目を展開する必要があります。

//steate    
expandedRows: [] //   
//render    
<Table
  bordered
  expandedRowKeys={this.state.expandedRows}
  onExpandedRowsChange={this.changeExpandedRows.bind(this)}
/>

//      ,       
changeExpandedRows = (expandedRows) => {
  this.setState({
   expandedRows
  })
 };

//     
let rows = this.state.expandedRows;
rows.push(record.key);
this.setState({
 expandedRows: rows
});

行データを削除
antdで削除するのは比較的簡単です。antdのテーブルは行ごとにkey属性がありますので、keyは唯一で必要な属性です。目的keyを含むデータをフィルタリングすれば削除と見なされます。これはツリーデータなので、通常の遍歴では操作できませんので、従来の深さ優先パスを使ってデータ処理を行うこともできますし、広さ優先を使ってデータによって変更することもできます。

handleDelete = (key) => {
  let data = this.state.data;
  data = dsFilter(data, key);

  this.setState({
   data
  });

  function dsFilter(dealData, dealKey) {
   for (let i = 0; i < dealData.length; i++) {
    if (dealData[i].children && dealData[i].children.length > 0) {
     dealData[i].children = dsFilter(dealData[i].children, dealKey);
    }
   }
   return dealData.filter(item => item.key !== dealKey);
  }
 };

いくつかのフィールドを変更
修正と追加のcolumsは同じですが、修正は保存前の値が必要です。inputまたはselectにdefaultValue={text}を設定して初期値が同じであることを保証する必要があります。
保存
新たに変更しても削除しても保存が必要です。ここではϫと𘚧を使って確定とキャンセルを代表します。
行の編集で他の行の操作をクリックすると、前に進めた操作をキャンセルと見なしますので、注意してください。

<div>
 <Button shape="circle" icon="check" size={'small'} style={{ backgroundColor: '#65BF34', color: '#FFF', border: 'none' }}
   onClick={this.saveEdit.bind(this, record.key)}
 />
 <Button shape="circle" icon="close" size={'small'} style={{ backgroundColor: '#FF3333', color: '#FFF', border: 'none' }}
   onClick={this.cancelEdit.bind(this, record.key)}
 />
</div>
順序を変更
順序を変更すると、データの配列の順序を変更し、簡単なtempを使って交換すればいいです。

shiftUp = (key) => {
  let data = this.state.data;
  data = dsShift(data, key);
  this.setState({
   data
  });

  function dsShift(dealData, dealKey) {
   for (let i = 0; i < dealData.length; i++) {
    if (dealData[i].children && dealData[i].children.length > 0) {
     dealData[i].children = dsShift(dealData[i].children, dealKey);
    }
    if (dealData[i].key === dealKey) {
     if (i === 0) {
      message.warning('     !');
      break;
     }
     let temp = dealData[i - 1];
     dealData[i - 1] = dealData[i];
     dealData[i] = temp;
     break;
    }
   }
   return dealData;
  }
 };

 shiftDown = (key) => {
  let data = this.state.data;
  data = dsShift(data, key);
  this.setState({
   data
  });

  function dsShift(dealData, dealKey) {
   for (let i = 0; i < dealData.length; i++) {
    if (dealData[i].children && dealData[i].children.length > 0) {
     dealData[i].children = dsShift(dealData[i].children, dealKey);
    }
    if (dealData[i].key === dealKey) {
     if (i === dealData.length - 1) {
      message.warning('     !');
      break;
     }
     let temp = dealData[i + 1];
     dealData[i + 1] = dealData[i];
     dealData[i] = temp;
     break;
    }
   }
   return dealData;
  }
 };

締め括りをつける
表データの操作には変数の保存が必要です。ここでは書きすぎません。総じて言えば、antdはいい部品で、ほとんどの機能がそろっていますが、多くの細部はやはり自分で完璧にする必要があります。
補足知識:Ant-design、入れ子テーブルの非同期取得データ
アリのANt-designソースフレームを使用して、表の中にカバーテーブルを埋め込むには、ユーザが親テーブルの一行のデータをクリックした後、その行のkeyを取得し、その後、非同期にサブテーブルの内容を満たすためにバックグラウンドのデータを要求します。
このように書けば(関連コードは省略されます):

expandedRowRender = (record) => {
 
  dispatch({
    type: 'flow/getPlanList',
    payload: {
      contractId: record.contract_id, //             key
    },
    callback: () => {
      const {
       flow: { data },
      } = this.props; 
 
      this.setState({
       secData: data.list,
      });
 
      console.log("    (PlanList):" + JSON.stringify(this.state.secData));
    }
  });
  
  return (
    <Table
      columns={secColumns}
      dataSource={this.state.secData}
      pagination={false}
    />
  );
};
 
render() {
  return(
    <Card>
     {this.renderForm()}
     <div>
       <Table
        expandedRowRender={this.expandedRowRender}
        loading={loading}
        rowSelection={rowSelection}
        dataSource={list}
        columns={columns}
        pagination={paginationProps}
        scroll={{ x: 2500}}
        size = 'middle' 
        expandRowByClick={true}
        onSelect={this.seFn}
       />
     </div>
    </Card>
  ) 
}
その場合、継続的なイニシアチブ要求が発生します。

これは、expadedRowRenderが実際にTableコンポーネントのrender方法で呼び出されたものであり、React renderでdispatchを使うと繰り返し呼出される問題があり、dispach->set State->render->dispach->set State->renderは、ループを繰り返すからである。だからdispatchをone Expandに置くべきです。

onExpand = (expanded, record) => {
  const { dispatch } = this.props;
 
  dispatch({
    type: 'flow/getPlanList',
    payload: {
     contractId: record.contract_id,
    },
    callback: () => {
      const {
        flow: { data },
      } = this.props; 
 
      this.setState({
        secData: data.list ,
      });
 
     console.log("    (PlanList):" + JSON.stringify(this.state.secData));
    }
  });
}
しかし、単純にこのようにすると、新しい問題が発生します。サブテーブルのすべてのデータは同じになります。
本人の解決方法はキーを使うことです。

onExpand = (expanded, record) => {
  const { dispatch } = this.props;
 
  if (expanded === false) {
   //             ,         ,    ,
   //                      
   console.log("  !");
   this.setState({
    subTabData: {
     ...this.state.subTabData,
     [record.contract_id]: [] ,
    }
   });
  } else {
   console.log("  !");
   dispatch({
    type: 'flow/getPlanList',
    payload: {
     contractId: record.contract_id,
    },
    callback: () => {
     const {
      flow: { data },
     } = this.props; 
 
     this.setState({
      subTabData: {
       ...this.state.subTabData,
       [record.contract_id]: data.list ,
      }
     });
 
     console.log("    (PlanList):" + JSON.stringify(this.state.subTabData));
    }
   });
  }
 }
以上のこのReact Ant Design樹形表の複雑な添削操作は小編集が皆さんに共有した内容の全部です。参考にしてもらいたいです。どうぞよろしくお願いします。