shadcn-ahooks

useFusionTable

useFusionTable encapsulates the commonly used [Fusion Form](https://fusion.design/pc/component/basic/form) and [Fusion Table](https://fusion.design/pc/component/basic/table) data binding logic.

Overview

useFusionTable encapsulates the commonly used Fusion Form and Fusion Table data binding logic.

Documentation and Examples

Installation

Open in
pnpm dlx shadcn@latest add https://shadcn-ahooks.vercel.app/r/useFusionTable.json
npx shadcn@latest add https://shadcn-ahooks.vercel.app/r/useFusionTable.json
yarn shadcn@latest add https://shadcn-ahooks.vercel.app/r/useFusionTable.json
bun shadcn@latest add https://shadcn-ahooks.vercel.app/r/useFusionTable.json

useFusionTable encapsulates the commonly used Fusion Form and Fusion Table data binding logic.

useFusionTable is implemented based on useRequest. Before using it, you need to understand a few points that are different from useRequest:

  1. service receives two parameters, the first parameter is the paging data { current, pageSize, sorter, filters }, and the second parameter is the form data.
  2. The data structure returned by service must be { total: number, list: Item[] }.
  3. Additional tablePropspaginationProps and search fields will be returned to manage tables and forms.
  4. When refreshDeps changes, it will reset current to the first page and re-initiate the request.

Examples

Table management

useFusionTable will automatically manage the pagination data of Table, you only need to pass the returned tableProps and paginationProps to the corresponding components.

<Table columns={columns} rowKey="email" {...tableProps} />
<Pagination {...paginationProps} />

import { Pagination, Table } from '@alifd/next';
import React from 'react';
import useFusionTable from '@/src/hooks/ahooks/useFusionTable';

interface Item {
  name: {
    last: string;
  };
  email: string;
  phone: string;
  gender: 'male' | 'female';
}

interface Result {
  total: number;
  list: Item[];
}

const getTableData = ({ current, pageSize }): Promise<Result> => {
  const query = `page=${current}&size=${pageSize}`;

  return fetch(`https://randomuser.me/api?results=${pageSize}&${query}`)
    .then((res) => res.json())
    .then((res) => ({
      total: 55,
      list: res.results.slice(0, 10),
    }));
};

const AppList = () => {
  const { paginationProps, tableProps } = useFusionTable(getTableData);

  return (
    <>
      <Table {...tableProps} primaryKey="email">
        <Table.Column title="name" dataIndex="name.last" width={140} />
        <Table.Column title="email" dataIndex="email" width={500} />
        <Table.Column title="phone" dataIndex="phone" width={500} />
        <Table.Column title="gender" dataIndex="gender" width={500} />
      </Table>
      <Pagination style={{ marginTop: 16 }} {...paginationProps} />
    </>
  );
};

export default AppList;

Form and Table data binding

When useFusionTable receives the field instance, it will return a search object to handle form related events.

  • search.type supports switching between simple and advance
  • search.changeType, switch form type
  • search.submit submit form
  • search.reset reset the current form

In the following example, you can experience the data binding between form and table.

import React from 'react';
import { Table, Pagination, Field, Form, Input, Button, Icon } from '@alifd/next';
import useFusionTable from '@/src/hooks/ahooks/useFusionTable';

import ReactJson from 'react-json-view';

interface Item {
  name: {
    last: string;
  };
  email: string;
  phone: string;
  gender: 'male' | 'female';
}

interface Result {
  total: number;
  list: Item[];
}

const getTableData = ({ current, pageSize }, formData: Object): Promise<Result> => {
  let query = `page=${current}&size=${pageSize}`;
  Object.entries(formData).forEach(([key, value]) => {
    if (value) {
      query += `&${key}=${value}`;
    }
  });

  return fetch(`https://randomuser.me/api?results=${pageSize}&${query}`)
    .then((res) => res.json())
    .then((res) => ({
      total: 55,
      list: res.results.slice(0, 10),
    }));
};

const AppList = () => {
  const field = Field.useField([]);
  const { paginationProps, tableProps, search, loading, params } = useFusionTable(getTableData, {
    field,
  });
  const { type, changeType, submit, reset } = search;

  const advanceSearchForm = (
    <div>
      <Form
        inline
        style={{
          display: 'flex',
          alignItems: 'center',
          justifyContent: 'flex-end',
        }}
        field={field}
      >
        <Form.Item label="name:">
          <Input name="name" placeholder="name" />
        </Form.Item>
        <Form.Item label="email:">
          <Input name="email" placeholder="email" />
        </Form.Item>
        <Form.Item label="phone:">
          <Input name="phone" placeholder="phone" />
        </Form.Item>

        <Form.Item label=" ">
          <Form.Submit loading={loading} type="primary" onClick={submit}>
            Search
          </Form.Submit>
        </Form.Item>

        <Form.Item label=" ">
          <Button onClick={reset}>reset</Button>
        </Form.Item>

        <Form.Item label=" ">
          <Button text type="primary" onClick={changeType}>
            Simple Search
          </Button>
        </Form.Item>
      </Form>
    </div>
  );

  const searchForm = (
    <div>
      <Form
        inline
        style={{
          display: 'flex',
          alignItems: 'center',
          justifyContent: 'flex-end',
        }}
        field={field}
      >
        <Form.Item label=" ">
          <Input
            name="name"
            innerAfter={<Icon type="search" size="xs" onClick={submit} style={{ margin: 4 }} />}
            placeholder="enter name"
            onPressEnter={submit}
          />
        </Form.Item>

        <Form.Item label=" ">
          <Button text type="primary" onClick={changeType}>
            Advanced Search
          </Button>
        </Form.Item>
      </Form>
    </div>
  );

  return (
    <>
      {type === 'simple' ? searchForm : advanceSearchForm}
      <Table {...tableProps} primaryKey="email">
        <Table.Column title="name" dataIndex="name.last" width={140} />
        <Table.Column title="email" dataIndex="email" width={500} />
        <Table.Column title="phone" dataIndex="phone" width={500} />
        <Table.Column title="gender" dataIndex="gender" width={500} />
      </Table>
      <Pagination style={{ marginTop: 16 }} {...paginationProps} />
      <div style={{ background: '#f5f5f5', padding: 8, marginTop: 16 }}>
        <p>Current Table:</p>
        <ReactJson src={params[0]!} collapsed={2} />
        <p>Current Form:</p>
        <ReactJson src={params[1]!} collapsed={2} />
      </div>
    </>
  );
};

export default AppList;

Default Params

useFusionTable sets the initial value through defaultParams, defaultParams is an array, the first item is paging related parameters, and the second item is form related data. If there is a second value, we will initialize the form for you!

It should be noted that the initial form data can be filled with all the form data of simple and advance, and we will help you select the form data of the currently activated type.

The following example sets paging data and form data during initialization.

import { Button, Field, Form, Icon, Input, Pagination, Select, Table } from '@alifd/next';
import React from 'react';
import useFusionTable from '@/src/hooks/ahooks/useFusionTable';

import ReactJson from 'react-json-view';

interface Item {
  name: {
    last: string;
  };
  email: string;
  phone: string;
  gender: 'male' | 'female';
}

interface Result {
  total: number;
  list: Item[];
}

const getTableData = ({ current, pageSize }, formData: Object): Promise<Result> => {
  let query = `page=${current}&size=${pageSize}`;
  Object.entries(formData).forEach(([key, value]) => {
    if (value) {
      query += `&${key}=${value}`;
    }
  });

  return fetch(`https://randomuser.me/api?results=${pageSize}&${query}`)
    .then((res) => res.json())
    .then((res) => ({
      total: 55,
      list: res.results.slice(0, 10),
    }));
};

const AppList = () => {
  const field = Field.useField([]);
  const { paginationProps, tableProps, search, loading, params } = useFusionTable(getTableData, {
    field,
    defaultParams: [
      { current: 2, pageSize: 5 },
      { name: 'hello', email: 'abc@gmail.com', gender: 'female' },
    ],
    defaultType: 'advance',
  });
  const { type, changeType, submit, reset } = search;

  const advanceSearchForm = (
    <div>
      <Form
        inline
        style={{ display: 'flex', alignItems: 'center', justifyContent: 'flex-end' }}
        field={field}
      >
        <Form.Item label="name:">
          <Input name="name" placeholder="name" />
        </Form.Item>
        <Form.Item label="email:">
          <Input name="email" placeholder="email" />
        </Form.Item>
        <Form.Item label="phone:">
          <Input name="phone" placeholder="phone" />
        </Form.Item>

        <Form.Item label=" ">
          <Form.Submit loading={loading} type="primary" onClick={submit}>
            Search
          </Form.Submit>
        </Form.Item>

        <Form.Item label=" ">
          <Button onClick={reset}>reset</Button>
        </Form.Item>

        <Form.Item label=" ">
          <Button text type="primary" onClick={changeType}>
            Simple Search
          </Button>
        </Form.Item>
      </Form>
    </div>
  );

  const searchForm = (
    <div>
      <Form
        inline
        style={{ display: 'flex', alignItems: 'center', justifyContent: 'flex-end' }}
        field={field}
      >
        <Form.Item label=" ">
          <Select name="gender" defaultValue="all" onChange={submit}>
            <Select.Option value="all">all</Select.Option>
            <Select.Option value="male">male</Select.Option>
            <Select.Option value="female">female</Select.Option>
          </Select>
        </Form.Item>

        <Form.Item label=" ">
          <Input
            name="name"
            innerAfter={<Icon type="search" size="xs" onClick={submit} style={{ margin: 4 }} />}
            placeholder="enter name"
            onPressEnter={submit}
          />
        </Form.Item>

        <Form.Item label=" ">
          <Button text type="primary" onClick={changeType}>
            Advanced Search
          </Button>
        </Form.Item>
      </Form>
    </div>
  );

  return (
    <>
      {type === 'simple' ? searchForm : advanceSearchForm}
      <Table {...tableProps} primaryKey="email">
        <Table.Column title="name" dataIndex="name.last" width={140} />
        <Table.Column title="email" dataIndex="email" width={500} />
        <Table.Column title="phone" dataIndex="phone" width={500} />
        <Table.Column title="gender" dataIndex="gender" width={500} />
      </Table>
      <Pagination style={{ marginTop: 16 }} {...paginationProps} />
      <div style={{ background: '#f5f5f5', padding: 8, marginTop: 16 }}>
        <p>Current Table:</p>
        <ReactJson src={params[0]!} collapsed={2} />
        <p>Current Form:</p>
        <ReactJson src={params[1]!} collapsed={2} />
      </div>
    </>
  );
};

export default AppList;

Form Validation

Before the form is submitted, we will automatically validate the form data. If the verification fails, the request will not be initiated.

import React from 'react';
import { Table, Pagination, Field, Form, Input, Select, Icon } from '@alifd/next';
import useFusionTable from '@/src/hooks/ahooks/useFusionTable';

import ReactJson from 'react-json-view';

interface Item {
  name: {
    last: string;
  };
  email: string;
  phone: string;
  gender: 'male' | 'female';
}

interface Result {
  total: number;
  list: Item[];
}

const getTableData = ({ current, pageSize }, formData: Object): Promise<Result> => {
  let query = `page=${current}&size=${pageSize}`;
  Object.entries(formData).forEach(([key, value]) => {
    if (value) {
      query += `&${key}=${value}`;
    }
  });

  return fetch(`https://randomuser.me/api?results=${pageSize}&${query}`)
    .then((res) => res.json())
    .then((res) => ({
      total: 55,
      list: res.results.slice(0, 10),
    }));
};

const AppList = () => {
  const field = Field.useField([]);
  const { paginationProps, tableProps, search, params } = useFusionTable(getTableData, {
    field,
    defaultParams: [{ current: 1, pageSize: 10 }, { name: 'hello' }],
  });
  const { submit } = search;

  const searchForm = (
    <div>
      <Form
        inline
        style={{ display: 'flex', alignItems: 'center', justifyContent: 'flex-end' }}
        field={field}
      >
        <Form.Item label=" ">
          <Input
            name="name"
            innerAfter={<Icon type="search" size="xs" onClick={submit} style={{ margin: 4 }} />}
            placeholder="enter name"
            onPressEnter={submit}
            {...field.init('name', { rules: [{ required: true }] })}
          />
        </Form.Item>
      </Form>
    </div>
  );

  return (
    <>
      {searchForm}
      <Table {...tableProps} primaryKey="email">
        <Table.Column title="name" dataIndex="name.last" width={140} />
        <Table.Column title="email" dataIndex="email" width={500} />
        <Table.Column title="phone" dataIndex="phone" width={500} />
        <Table.Column title="gender" dataIndex="gender" width={500} />
      </Table>
      <Pagination style={{ marginTop: 16 }} {...paginationProps} />
      <div style={{ background: '#f5f5f5', padding: 8, marginTop: 16 }}>
        <p>Current Table:</p>
        <ReactJson src={params[0]!} collapsed={2} />
        <p>Current Form:</p>
        <ReactJson src={params[1]!} collapsed={2} />
      </div>
    </>
  );
};

export default AppList;

Data Caching

By setting cacheKey, we can apply the data caching for the Form and Table .

import React, { useState } from 'react';
import { Table, Pagination, Field, Form, Input, Button } from '@alifd/next';
import useFusionTable from '@/src/hooks/ahooks/useFusionTable';

import ReactJson from 'react-json-view';

interface Item {
  name: {
    last: string;
  };
  email: string;
  phone: string;
  gender: 'male' | 'female';
}

interface Result {
  total: number;
  list: Item[];
}

const getTableData = (
  { current, pageSize, filters, sorter },
  formData: Object,
): Promise<Result> => {
  console.log(sorter, filters);

  let query = `page=${current}&size=${pageSize}`;
  Object.entries(formData).forEach(([key, value]) => {
    if (value) {
      query += `&${key}=${value}`;
    }
  });

  return fetch(`https://randomuser.me/api?results=${pageSize}&${query}`)
    .then((res) => res.json())
    .then((res) => ({
      total: 55,
      list: res.results,
    }));
};

const AppList = () => {
  const field = Field.useField([]);

  const { tableProps, paginationProps, params, search } = useFusionTable(getTableData, {
    defaultPageSize: 5,
    field,
    cacheKey: 'tableProps',
  });

  const { filters = {} } = params[0] || {};
  const { type, changeType, submit, reset } = search || {};

  const searchFrom = (
    <div style={{ marginBottom: 16 }}>
      <Form
        field={field}
        inline
        style={{ display: 'flex', justifyContent: 'flex-end', alignItems: 'center' }}
      >
        <Form.Item label="name:">
          <Input name="name" placeholder="name" />
        </Form.Item>
        {type === 'advance' && (
          <>
            <Form.Item label="email:">
              <Input name="email" placeholder="email" />
            </Form.Item>
            <Form.Item label="phone:">
              <Input name="phone" placeholder="phone" />
            </Form.Item>
          </>
        )}
        <Form.Item label=" ">
          <Form.Submit type="primary" onClick={submit}>
            Search
          </Form.Submit>
        </Form.Item>

        <Form.Item label=" ">
          <Button onClick={reset}>reset</Button>
        </Form.Item>

        <Form.Item label=" ">
          <Button text type="primary" onClick={changeType}>
            {type === 'simple' ? 'Expand' : 'Close'}
          </Button>
        </Form.Item>
      </Form>
    </div>
  );

  return (
    <div>
      {searchFrom}

      <Table {...tableProps} filterParams={filters} primaryKey="email">
        <Table.Column title="name" dataIndex="name.last" width={140} />
        <Table.Column title="email" dataIndex="email" width={500} />
        <Table.Column title="phone" sortable dataIndex="phone" width={500} />
        <Table.Column
          title="gender"
          filters={[
            { label: 'male', value: 'male' },
            { label: 'female', value: 'female' },
          ]}
          dataIndex="gender"
          width={500}
        />
      </Table>
      <Pagination style={{ marginTop: 16 }} {...paginationProps} />
      <div style={{ background: '#f5f5f5', padding: 8, marginTop: 16 }}>
        <p>Current Table:</p>
        <ReactJson src={params[0]!} collapsed={2} />
        <p>Current Form:</p>
        <ReactJson src={params[1]!} collapsed={2} />
      </div>
    </div>
  );
};

const Demo = () => {
  const [show, setShow] = useState(true);

  return (
    <div>
      <Button
        type="primary"
        warning
        onClick={() => {
          setShow(!show);
        }}
        style={{ marginBottom: 16 }}
      >
        {show ? 'Click to destroy' : 'Click recovery'}
      </Button>
      {show && <AppList />}
    </div>
  );
};

export default Demo;

API

All parameters and returned results of useRequest are applicable to useFusionTable, so we won't repeat them here.


type Data = { total: number; list: any[] };
type Params = [{ current: number; pageSize: number, filter?: any, sorter?: any }, { [key: string]: any }];

const {
  ...,
  tableProps: {
    dataSource: TData['list'];
    loading: boolean;
    onSort: (dataIndex: string, order: string) => void;
    onFilter: (filterParams: any) => void;
  };
  paginationProps: {
    onChange: (current: number) => void;
    onPageSizeChange: (size: number) => void;
    current: number;
    pageSize: number;
    total: number;
  };
  search: {
    type: 'simple' | 'advance';
    changeType: () => void;
    submit: () => void;
    reset: () => void;
  };
} = useFusionTable<TData extends Data, TParams extends Params>(
  service: (...args: TParams) => Promise<TData>,
  {
    ...,
    field?: any;
    defaultType?: 'simple' | 'advance';
    defaultParams?: TParams,
    defaultPageSize?: number;
    refreshDeps?: any[];
  }
);

Result

| Property | Description | Type | | -- | | | | -- | | field | Form instance | - | - | | defaultType | Default form type | simple | advance | simple | | defaultParams | Default parameters, the first item is paging data, the second item is form data | [pagination, formData] | - | | defaultPageSize | Default page size | number | 10 | | refreshDeps | Changes in refreshDeps will reset current to the first page and re-initiate the request. | React.DependencyList | [] |

On this page