React Hooks實踐--抽離組件中的可複用邏輯

先看需求:

系統中的若干個頁面須要展現表格數據,其中某些表格數據過多須要分頁查詢,而某些不用。javascript

組件劃分:

components:
    TableDisplay.jsx 
    Pagination.jsx
containers:
    TableContainer.jsx

TableDisplay:表格數據展現組件,參數以下:

  • data: Array類型,須要展現的表格數據
  • loading: Boolean類型,控制表格是否加載狀態

Pagination:用於展現分頁的組件,參數以下:

  • total: Number類型,總數據量
  • page: Number類型,當前頁碼
  • pageSize: Number類型,每頁展現的數據量
  • onPageChange: Function類型,頁碼改變時的回調
  • onPageSizeChange: Function類型,pageSize改變時的回調

TableContainer:封裝分頁控制、加載表格數據等業務邏輯的容器型組件

一、 使用Class封裝業務邏輯的組件

首先看下不使用hooks,使用傳統的Class組件封裝業務邏輯的代碼:java

import React from 'react';
import TableDisplay from 'components/TableDisplay';
import Pagination from 'components/Pagination';
import { getTableData } from 'apis';    //獲取表格數據的api

export default class TableContainer extends React.Component {
    state = {
        data: {
            tableData: [],
            total: 0,
        },
        pagination: {
            page: 1,
            pageSize: 10
        },
        loading: false
    }
    
    componentDidMount() {
        // 首次加載時默認查詢第一頁數據
        this.loadTable();
    }
    
    handlePageChange: (page) => {
        this.setState({
            pagination: {
                ...this.state.pagination, page
            }
        });
        this.loadTable();
    }
    
    handlePageSizeChange: (pageSize) => {
        this.setState({
            pagination: {
                page: 1,    //pageSize改變時,page自動跳到1
                pageSize
            }
        });
        this.loadTable();
    }
    
    loadTable: () => {
        this.setState({
            loading:true
        });
        
        // 調用APi獲取數據
        getTableData(this.state.pagination)
            .then(({data}) => {
                // 數據加載成功
                this.setState({
                    data
                });
            })
            .catch(error => {
                console.log(error);
            })
            .finally(() => {
                this.setState({
                    loading: false
                });
            });
    }
    
    render() {
        const {data, pagination, loading} = this.state;
        
        return (
            <div>
                <TableDisplay data={ data.tableData } loading={ loading } />
                <Pagination
                    onPageChange={ this.handlePageChange }
                    onPageSizeChange={ this.handlePageSizeChange }
                    total={ data.total }
                    { ...pagination } />
            </div>
        );
    }
}
複製代碼

使用Class組件使得業務邏輯代碼難以分割和複用;react

二、使用帶Hooks 的Function組件改寫

import React, { useState, useEffect } from 'react';
import TableDisplay from 'components/TableDisplay';
import Pagination from 'components/Pagination';
import { getTableData } from 'apis';    //獲取表格數據的api

export default () => {
    // 表格數據
    const [data, setData] = useState({
        tableData: [],
        total: 0
    });
    // 分頁狀態
    const [pagination, setPagination] = useState({
        page: 1,
        pageSize: 10
    });
    // 加載狀態
    const [loading, setLoading] = useState(false);
    
    useEffect(() => {   //分頁狀態改變時,加載數據
        setLoading(true);
        getTableData(pagination)
            .then(data => {
                setTableData({
                    total: data.total,
                    data: data.data
                });
            })
            .catch(error => {
                console.log(error);
            })
            .finally(() => {
                setLoading(false);
            });
    }, pagination); 
    
    const handlePageChange = page => setPagination({
        page,
        pageSize: pagination.pageSize
    });
    
    const handlePageSizeChange = pageSize => setPagination({
        page: 1,
        pageSize
    });
    
    return (
        <div>
            <TableDisplay data={ data.tableData } loading={ loading } />
            <Pagination
                onPageChange={ handlePageChange }
                onPageSizeChange={ handlePageSizeChange }
                total={ data.total }
                { ...pagination } />
        </div>
    );
}

複製代碼

到這一步,還沒達到抽離可複用邏輯的目的api

三、使用自定義Hooks分割並抽離業務邏輯

爲了實現本文第一行中提到的需求,須要將表格數據加載和分頁控制分割開來,使得分頁這部分功能可插拔。ui

因而須要兩個自定義Hookthis

hooks:
    --useTableDataLoader
    --usePagination

useTableDataLoader.js代碼以下:

import { useState, useEffect } from 'react';

export default (api, pagination) => {
    const [data, setData] = useState({
        total: 0,
        tableData: []
    });
    const [loading, setLoading] = useState(false);

    useEffect(() => {
        setLoading(true);
        api(pagination)
            .then(data => {
                setData(data);
            })
            .catch(error => {
                console.log(error)
            })
            .finally(() => {
                setLoading(false);
            });
    }, pagination);
    
    return {
        data, loading
    };
}

複製代碼

HookuseTableDataLoader須要兩個參數:spa

  • api:獲取表格數據的api
  • pagination:分頁狀態,可選參數,當api不須要時可不傳入,表格只會加載一次

返回一個對象:code

  • data: 請求的數據
  • loading:是否處於加載狀態

usePagination.js代碼以下:

import { useState } from 'react';

export default () => {
    const [pagination, setPagination] = useState({
        page: 1,
        pageSize: 10
    });

    return {
        pagination,
        setPage(page) {
            setPagination({
                page,
                pageSize: pagination.pageSize
            });
        },
        setPageSize(pageSize) {
            setPagination({
                page: 1,
                pageSize
            });
        }
    };
}
複製代碼

HookuseTableDataLoader返回一個對象:component

  • pagination:分頁信息
  • setPage:修改當前頁碼的方法
  • setPageSize:修改pageSize的方法

四、使用自定義的Hook實現帶分頁控制的表格業務組件

TableWithPaginationContainer.jsx代碼以下:

import React from 'react';
import TableDisplay from 'components/TableDisplay';
import Pagination from 'components/Pagination';
import { getTableData } from 'apis';    //獲取表格數據的api
import useTableDataLoader from 'hooks/useTableDataLoader';
import usePagination from 'hooks/usePagination';

export default () => {
    const { pagination, setPage, setPageSize } = usePagination();
    const { data, loading } = useTableDataLoader(getTableData, pagination);
    const { tableData, total } = data;
    return (
        <div>
            <TableDisplay loading={loading} data={tableData}/>
            <Pagination
                onPageChange={ setPage }
                onPageSizeChange={ setPageSize }
                total={total}
                { ...pagination } />
        </div>
    );
}

複製代碼

五、無分頁控制的表格業務組件

TableContainer.jsx代碼以下:

import React from 'react';
import TableDisplay from 'components/TableDisplay';
import { getTableData } from 'apis';    //獲取表格數據的api
import useTableDataLoader from 'hooks/useTableDataLoader';

export default () => {
    const { data, loading } = useTableDataLoader(getTableData);
    const { tableData, total } = data;
    return (
        <TableDisplay loading={loading} data={tableData}/> ); } 複製代碼

這樣就實現了可複用邏輯代碼的分割和抽離,並使得編寫組件的代碼更加簡介明瞭!xml

相關文章
相關標籤/搜索