In the background management system, we often encounter the following business scenarios:

The top is search, the bottom is paginated table. The Ant Design component library has nicely wrapped the table component and the paging component together. We just need to change the column, dataSource, and other properties to implement different pages. As you write more pages, you will find that there is still some repetitive logic, that is, the logic of requesting data: when the page number is changed, you need to request data again; The logic that requests data is triggered when the query condition is modified. With this in mind, we extracted the logic and encapsulated a table group containing the request data with the ant Design component. Effect:

Initialize the

npx create-react-app page-table
npm install antd -D
Copy the code

Create-react-app initializes the project and installs the ANTD component library.

The fetch and XMLHttpRequest

In fact, we can use umi-Request and Axios to send requests. This paper USES the native fetch requests (as long as it is in order to study 😄), recommend nguyen piece “fetch API tutorial” blog (www.ruanyifeng.com/blog/2020/1)… And MDN documentation (developer.mozilla.org/zh-CN/docs/…) .

Here are a few summaries I used:

  1. Fetch will return onePromiseObject. Even if the back end returns 404 or 500, no error is reported, it just returnsPromiseObject marked asresolvedAnd sets the OK property of the return value of resolve to false. It is marked reject only when the network fails or the request is blocked.

  1. When across domains, neither FETCH nor XHR will automatically bring cookies. The FETCH request requires setting the credentials: ‘include’. XMLHttpRequest requests require the following Settings: xhr.withCredentials = true;

  2. AbortController can cancel the fetch request. The XMLHttpRequest request is canceled through xhr.abort().

  3. AbortController Cancelling the FETCH request results in a REJECT Promise state returned by the FETCH.

See the following code for details:

  const url = "http://localhost:8081/pageTable/v1/famousInfo2? current=1&pageSize=8"
  const xhr = new XMLHttpRequest();
  xhr.onreadystatechange = function () {
      console.log(xhr, '-- respone')}// Request a Cookie
  xhr.withCredentials = true;
  xhr.open('GET', url, true);
  xhr.send();
  // Cancel the request
  setTimeout(() = > {
      xhr.abort();
  }, 1500);

  const controller = new AbortController();
  const signal = controller.signal;
  fetch(url, {
      method: 'GET'.mode: 'cors'.credentials: 'include'.signal: signal
  }).then(res= > {
      console.log(res);
  });

  setTimeout(() = > {
      controller.abort();
  }, 1500)
Copy the code

More detailed XMLHttpRequest request: segmentfault.com/a/119000000…

The node interface

Exress is used to write a Restful query interface. Set up the cross-domain code 👇 :

// Set cross-domain access
app.all("*".function (request, response, next) {
  * indicates that any domain name is allowed to cross domains. http://localhost:3000 indicates the Origin address of the front-end request
  response.header("Access-Control-Allow-Origin"."http://localhost:3000");
  // Set what attributes can be added to the request header
  response.header('Access-Control-Allow-Headers'.'Content-Type, Content-Length, Authorization, Accept, X-Requested-With');
  // Set Cookie information to be carried across domains
  response.header('Access-Control-Allow-Credentials'."true");
  // Sets which methods in the request header are valid
  response.header(
    "Access-Control-Allow-Methods"."PUT,POST,GET,DELETE,OPTIONS"
  );
  response.header("Content-Type"."application/json; charset=utf-8");
  next();
});
Copy the code

page-table

API

Without further ado, here is the API for using the wrapped form component (which comes with request data) :

attribute instructions type The default value
url mandatory. The URL for the requested data String
columns mandatory. Configuration description of table columns Array []
pageInfo Optional. The paging parameter object for the request Object {current: 1,pageSize: 8}
params Optional. The parameter object of the request Object {}
isPagination Optional. Do I need paging Boolean true
type Optional. The first column shows the check or radio, optional values have the checkbox | radio. Nothing will show if you don’t fill it in. String
rowKey Optional. Table row key value; By default, the ROW_ID field is added to the table data as the key for the row data. String “ROW_ID”

A comparative event API: onSelectChange; If the first column is a checkbox | radio clicked the callback function.

  const onSelectChange = (selectedRowKeys, selectedRows) = >{
      console.log(selectedRowKeys, selectedRows);
  }
Copy the code

The selectedRowKeys argument means that the rowKeys array is clicked on the selected row; SelectedRows represents clicking on the Rows array of selected data rows.

Send data request

It is not difficult to think of using the useEffect hook function to send a request when the page loads and to cancel the request being sent when the page unloads. Here are three very clever points:

  • 🍓 useEffect (() = > {the fetch ()}, [url, params, pageOpitons]); Subtly add the dependent variable to the second parameter. Where URL and params are parameters passed in from the external parent component, that is, when the URL of the parent component or the query parameter params is modified, the Page-table component will be triggered to request background data again. PageOpitons are state data held by the Page-Table component, that is, the Page-Table component can switch pages to trigger the re-request of background data.

  • 🍒 For pagination of the page-table component, the parent component can also modify pageInfo to trigger pagination modification. React Hook changes the parent props to trigger state changes of the child components. How do you solve it? Take a look at the following code 👇 :

  // Initialize pageOpitons in useState
  const [pageOpitons, setPageOptions] = useState({
    current: pageInfo && pageInfo.current ?  pageInfo.current : 1.pageSize: pageInfo && pageInfo.pageSize ? pageInfo.pageSize : 8
  });
  
  useEffect(() = >{
    // When pageInfo changes and has a value, check whether it is the same as pageOpitons.
    // Avoid repeated requests caused by repeated assignments.
    // isEqual checks whether the key and value of pageInfo and pageOpitons are the same; Same means same.
    if(pageInfo && ! isEqual(pageInfo, pageOpitons)){ setPageOptions({current: pageInfo.current,
        pageSize: pageInfo.pageSize
      })
    }
  }, [pageInfo]);
Copy the code
// common.js
function isObject(obj) {
    return typeof obj === 'object'&& obj ! = =null;
}
// Check whether obj1 and obj2 are identical: key and value are identical
export function isEqual(obj1, obj2) {
    if(! isObject(obj1) && ! isObject(obj1)) {// Value type comparison
        return obj1 === obj2;
    }
    if (obj1 === obj2) {
        return true;
    }
    const keys1 = Object.keys(obj1);
    const keys2 = Object.keys(obj2);
    if(keys1.length ! == keys2.length) {return false;
    }
    for (let key of keys1) {
        const res = isEqual(obj1[key], obj2[key]);
        if(! res) {returnres; }}return true;
}
Copy the code
  • Cancel sending requests while switching 🍍 pages. This time, mentioned aboveAbortControllerIt works. usinguseEffect(()=>{ return ()=>{ abort();}}, []);simulationcomponentWillUnmountAnd pay attention to the use of ❗️useRefTo retainAbortControllerObject, so that the cancellation is the same.
  const abortControllerRef = useRef(new AbortController());
  // Cancel the request
  useEffect(() = > {
    return () = > abortControllerRef.current.abort()
  }, []);
Copy the code

The source address

Git: github.com/YY88Xu/page… Welcome to fork your valuable suggestions ❤️.