偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

一篇讓我們學(xué)會(huì)React實(shí)踐

開發(fā) 前端
每天都在寫業(yè)務(wù)代碼中度過,但是呢,經(jīng)常在寫業(yè)務(wù)代碼的時(shí)候,會(huì)感覺自己寫的某些代碼有點(diǎn)別扭,但是又不知道是哪里別扭,今天這篇文章我整理了一些在項(xiàng)目中使用的一些小的技巧點(diǎn)。

[[413977]]

每天都在寫業(yè)務(wù)代碼中度過,但是呢,經(jīng)常在寫業(yè)務(wù)代碼的時(shí)候,會(huì)感覺自己寫的某些代碼有點(diǎn)別扭,但是又不知道是哪里別扭,今天這篇文章我整理了一些在項(xiàng)目中使用的一些小的技巧點(diǎn)。

狀態(tài)邏輯復(fù)用

在使用React Hooks之前,我們一般復(fù)用的都是組件,對(duì)組件內(nèi)部的狀態(tài)是沒辦法復(fù)用的,而React Hooks的推出很好的解決了狀態(tài)邏輯的復(fù)用,而在我們?nèi)粘i_發(fā)中能做到哪些狀態(tài)邏輯的復(fù)用呢?下面我羅列了幾個(gè)當(dāng)前我在項(xiàng)目中用到的通用狀態(tài)復(fù)用。

useRequest

為什么要封裝這個(gè)hook呢?在數(shù)據(jù)加載的時(shí)候,有這么幾點(diǎn)是可以提取成共用邏輯的

  • loading狀態(tài)復(fù)用
  • 異常統(tǒng)一處理
  1. const useRequest = () => { 
  2.   const [loading, setLoading] = useState(false); 
  3.   const [error, setError] = useState(); 
  4.  
  5.   const run = useCallback(async (...fns) => { 
  6.     setLoading(true); 
  7.     try { 
  8.       await Promise.all
  9.         fns.map((fn) => { 
  10.           if (typeof fn === 'function') { 
  11.             return fn(); 
  12.           } 
  13.           return fn; 
  14.         }) 
  15.       ); 
  16.     } catch (error) { 
  17.       setError(error); 
  18.     } finally { 
  19.       setLoading(false); 
  20.     } 
  21.   }, []); 
  22.  
  23.   return { loading, error, run }; 
  24. }; 
  25.  
  26. function App() { 
  27.   const { loading, error, run } = useRequest(); 
  28.   useEffect(() => { 
  29.     run( 
  30.       new Promise((resolve) => { 
  31.         setTimeout(() => { 
  32.           resolve(); 
  33.         }, 2000); 
  34.       }) 
  35.     ); 
  36.   }, []); 
  37.   return ( 
  38.     <div className="App"
  39.       <Spin spinning={loading}> 
  40.         <Table columns={columns} dataSource={data}></Table
  41.       </Spin> 
  42.     </div> 
  43.   ); 

usePagination

我們用表格的時(shí)候,一般都會(huì)用到分頁,通過將分頁封裝成hook,一是可以介紹前端代碼量,二是統(tǒng)一了前后端分頁的參數(shù),也是對(duì)后端接口的一個(gè)約束。

  1. const usePagination = ( 
  2.   initPage = { 
  3.     total: 0, 
  4.     current: 1, 
  5.     pageSize: 10, 
  6.   } 
  7. ) => { 
  8.   const [pagination, setPagination] = useState(initPage); 
  9.  
  10.   // 用于接口查詢數(shù)據(jù)時(shí)的請(qǐng)求參數(shù) 
  11.   const queryPagination = useMemo( 
  12.     () => ({ limit: pagination.pageSize, offset: pagination.current - 1 }), 
  13.     [pagination.current, pagination.pageSize] 
  14.   ); 
  15.  
  16.   const tablePagination = useMemo(() => { 
  17.     return { 
  18.       ...pagination, 
  19.       onChange: (page, pageSize) => { 
  20.         setPagination({ 
  21.           ...pagination, 
  22.           current: page, 
  23.           pageSize, 
  24.         }); 
  25.       }, 
  26.     }; 
  27.   }, [pagination]); 
  28.  
  29.   const setTotal = useCallback((total) => { 
  30.     setPagination((prev) => ({ 
  31.       ...prev, 
  32.       total, 
  33.     })); 
  34.   }, []); 
  35.   const setCurrent = useCallback((current) => { 
  36.     setPagination((prev) => ({ 
  37.       ...prev, 
  38.       current
  39.     })); 
  40.   }, []); 
  41.  
  42.   return { 
  43.     // 用于antd 表格使用 
  44.     pagination: tablePagination, 
  45.     // 用于接口查詢數(shù)據(jù)使用 
  46.     queryPagination, 
  47.     setTotal, 
  48.     setCurrent, 
  49.   }; 
  50. }; 

除了上面示例的兩個(gè)hook,其實(shí)自定義hook可以無處不在,只要有公共的邏輯可以被復(fù)用,都可以被定義為獨(dú)立的hook,然后在多個(gè)頁面或組件中使用,我們?cè)谑褂胷edux,react-router的時(shí)候,也會(huì)用到它們提供的hook。

在合適場(chǎng)景給useState傳入函數(shù)

我們?cè)谑褂胾seState的setState的時(shí)候,大部分時(shí)候都會(huì)給setState傳入一個(gè)值,但實(shí)際上setState不但可以傳入普通的數(shù)據(jù),而且還可以傳入一個(gè)函數(shù)。下面極端代碼分別描述了幾個(gè)傳入函數(shù)的例子。

下面的代碼3秒后輸出什么?

如下代碼所示,也有有兩個(gè)按鈕,一個(gè)按鈕會(huì)在點(diǎn)擊后延遲三秒然后給count + 1, 第二個(gè)按鈕會(huì)在點(diǎn)擊的時(shí)候,直接給count + 1,那么假如我先點(diǎn)擊延遲的按鈕,然后多次點(diǎn)擊不延遲的按鈕,三秒鐘之后,count的值是多少?

  1. import { useState, useEffect } from 'react'
  2.  
  3. function App() { 
  4.   const [count, setCount] = useState(0); 
  5.  
  6.   function handleClick() { 
  7.     setTimeout(() => { 
  8.       setCount(count + 1); 
  9.     }, 3000); 
  10.   } 
  11.  
  12.   function handleClickSync() { 
  13.     setCount(count + 1); 
  14.   } 
  15.  
  16.   return ( 
  17.     <div className="App"
  18.       <div>count:{count}</div> 
  19.       <button onClick={handleClick}>延遲加一</button> 
  20.       <button onClick={handleClickSync}>加一</button> 
  21.     </div> 
  22.   ); 
  23.  
  24. export default App; 

我們知道,React的函數(shù)式組件會(huì)在自己內(nèi)部的狀態(tài)或外部傳入的props發(fā)生變化時(shí),做重新渲染的動(dòng)作。實(shí)際上這個(gè)重新渲染也就是重新執(zhí)行這個(gè)函數(shù)式組件。

當(dāng)我們點(diǎn)擊延遲按鈕的時(shí)候,因?yàn)閏ount的值需要三秒后才會(huì)改變,這時(shí)候并不會(huì)重新渲染。然后再點(diǎn)擊直接加一按鈕,count值由1變成了2, 需要重新渲染。這里需要注意的是,雖然組件重新渲染了,但是setTimeout是在上一次渲染中被調(diào)用的,這也意味著setTimeout里面的count值是組件第一次渲染的值。

所以即使第二個(gè)按鈕加一多次,三秒之后,setTimeout回調(diào)執(zhí)行的時(shí)候因?yàn)橐玫腸ount的值還是初始化的0, 所以三秒后count + 1的值就是1

如何讓上面的代碼延遲三秒后輸出正確的值?

這時(shí)候就需要使用到setState傳入函數(shù)的方式了,如下代碼:

  1. import { useState, useEffect } from 'react'
  2.  
  3. function App() { 
  4.   const [count, setCount] = useState(0); 
  5.  
  6.   function handleClick() { 
  7.     setTimeout(() => { 
  8.       setCount((prevCount) => prevCount + 1); 
  9.     }, 3000); 
  10.   } 
  11.  
  12.   function handleClickSync() { 
  13.     setCount(count + 1); 
  14.   } 
  15.  
  16.   return ( 
  17.     <div className="App"
  18.       <div>count:{count}</div> 
  19.       <button onClick={handleClick}>延遲加一</button> 
  20.       <button onClick={handleClickSync}>加一</button> 
  21.     </div> 
  22.   ); 
  23.  
  24. export default App; 

從上面代碼可以看到,setCount(count + 1)被改為了setCount((prevCount) => prevCount + 1)。我們給setCount傳入一個(gè)函數(shù),setCount會(huì)調(diào)用這個(gè)函數(shù),并且將前一個(gè)狀態(tài)值作為參數(shù)傳入到函數(shù)中,這時(shí)候我們就可以在setTimeout里面拿到正確的值了。

還可以在useState初始化的時(shí)候傳入函數(shù)

看下面這個(gè)例子,我們有一個(gè)getColumns函數(shù),會(huì)返回一個(gè)表格的所以列,同時(shí)有一個(gè)count狀態(tài),每一秒加一一次。

  1. function App() { 
  2.   const columns = getColumns(); 
  3.   const [count, setCount] = useState(0); 
  4.   useEffect(() => { 
  5.     setInterval(() => { 
  6.       setCount((prevCount) => prevCount + 1); 
  7.     }, 1000); 
  8.   }, []); 
  9.  
  10.   useEffect(() => { 
  11.     console.log('columns發(fā)生了變化'); 
  12.   }, [columns]); 
  13.   return ( 
  14.     <div className="App"
  15.       <div>count: {count}</div> 
  16.       <Table columns={columns}></Table
  17.     </div> 
  18.   ); 

上面的代碼執(zhí)行之后,會(huì)發(fā)現(xiàn)每次count發(fā)生變化的時(shí)候,都會(huì)打印出columns發(fā)生了變化,而columns發(fā)生變化便意味著表格的屬性發(fā)生變化,表格會(huì)重新渲染,這時(shí)候如果表格數(shù)據(jù)量不大,沒有復(fù)雜處理邏輯還好,但如果表格有性能問題,就會(huì)導(dǎo)致整個(gè)頁面的體驗(yàn)變得很差?其實(shí)這時(shí)候解決方案有很多,我們看一下如何用useState來解決呢?

  1. // 將columns改為如下代碼 
  2. const [columns] = useState(() => getColumns()); 

這時(shí)候columns的值在初始化之后就不會(huì)再發(fā)生變化了。有人提出我也可以這樣寫 useState(getColumns()), 實(shí)際這樣寫雖然也可以,但是假如getColumns函數(shù)自身存在復(fù)雜的計(jì)算,那么實(shí)際上雖然useState自身只會(huì)初始化一次,但是getColumn還是會(huì)在每次組件重新渲染的時(shí)候被執(zhí)行。

上面的代碼也可以簡(jiǎn)化為

  1. const [columns] = useState(getColumns); 

了解hook比較算法的原理

  1. const useColumns = (options) => { 
  2.   const { isEdit, isDelete } = options; 
  3.   return useMemo(() => { 
  4.     return [ 
  5.       { 
  6.         title: '標(biāo)題'
  7.         dataIndex: 'title'
  8.         key'title'
  9.       }, 
  10.       { 
  11.         title: '操作'
  12.         dataIndex: 'action'
  13.         key'action'
  14.         render() { 
  15.           return ( 
  16.             <> 
  17.               {isEdit && <Button>編輯</Button>} 
  18.               {isDelete && <Button>刪除</Button>} 
  19.             </> 
  20.           ); 
  21.         }, 
  22.       }, 
  23.     ]; 
  24.   }, [options]); 
  25. }; 
  26.  
  27. function App() { 
  28.   const columns = useColumns({ isEdit: true, isDelete: false }); 
  29.   const [count, setCount] = useState(1); 
  30.  
  31.   useEffect(() => { 
  32.     console.log('columns變了'); 
  33.   }, [columns]); 
  34.   return ( 
  35.     <div className="App"
  36.       <div> 
  37.         <Button onClick={() => setCount(count + 1)}>修改count:{count}</Button> 
  38.       </div> 
  39.       <Table columns={columns} dataSource={[]}></Table
  40.     </div> 
  41.   ); 

如上面的代碼,當(dāng)我們點(diǎn)擊按鈕修改count的時(shí)候,我們期待只有count的值會(huì)發(fā)生變化,但是實(shí)際上columns的值也發(fā)生了變化。想了解為什么columns會(huì)發(fā)生變化,我們先了解一下react比較算法的原理。

react比較算法底層是使用的Object.is來比較傳入的state的.

語法: Object.is(value1, value2);

如下代碼是Object.is比較不同數(shù)據(jù)類型的數(shù)據(jù)時(shí)的返回值:

  1. Object.is('foo''foo');     // trueObject.is(window, window);   // trueObject.is('foo''bar');     // falseObject.is([], []);           // falsevar foo = { a: 1 };var bar = { a: 1 };Object.is(foo, foo);         // trueObject.is(foo, bar);         // falseObject.is(nullnull);       // true// 特例Object.is(0, -0);            // falseObject.is(0, +0);            // trueObject.is(-0, -0);           // trueObject.is(NaN, 0/0);         // true 

通過上面的代碼可以看到,Object.is對(duì)于對(duì)象的比較是比較引用地址的,而不是比較值的,所以O(shè)bject.is([], []), Object.is({},{})的結(jié)果都是false。而對(duì)于基礎(chǔ)類型來說,大家需要注意的是最末尾的四個(gè)特列,這是與===所不同的。

再回到上面代碼的例子中,useColumns將傳入的options作為useMemo的第二個(gè)參數(shù),而options是一個(gè)對(duì)象。當(dāng)組件的count狀態(tài)發(fā)生變化的時(shí)候,會(huì)重新執(zhí)行整個(gè)函數(shù)組件,這時(shí)候useColumns會(huì)被調(diào)用然后傳入{ isEdit: true, isDelete: false },這是一個(gè)新創(chuàng)建的對(duì)象,與上一次渲染所創(chuàng)建的options的內(nèi)容雖然一致,但是Object.is比較結(jié)果依然是false,所以columns的結(jié)果會(huì)被重新創(chuàng)建返回。

通過二次封裝標(biāo)準(zhǔn)化組件

我們?cè)陧?xiàng)目中使用antd作為組件庫,雖然antd可以滿足大部分的開發(fā)需要,但是有些地方通過對(duì)antd進(jìn)行二次封裝,不僅可以減少開發(fā)代碼量,而且對(duì)于頁面的交互起到了標(biāo)準(zhǔn)化作用。

看一下下面這個(gè)場(chǎng)景, 在我們開發(fā)一個(gè)數(shù)據(jù)表格的時(shí)候,一般會(huì)用到哪些功能呢?

  1. 表格可以分頁
  2. 表格最后一列會(huì)有操作按鈕
  3. 表格頂部會(huì)有搜索區(qū)域
  4. 表格頂部可能會(huì)有操作按鈕

還有其他等等一系列的功能,這些功能在系統(tǒng)中會(huì)大量使用,而且其實(shí)現(xiàn)方式基本是一致的,這時(shí)候如果能把這些功能集成到一起封裝成一個(gè)標(biāo)準(zhǔn)的組件,那么既能減少代碼量,而且也會(huì)讓頁面展現(xiàn)上更加統(tǒng)一。

以封裝表格操作列為例,一般用操作列我們會(huì)像下面這樣封裝

  1. const columns = [{        title: '操作',        dataIndex: 'action',        key'action',        width: '10%',        align: 'center',        render: (_, row) => {          return (            <>              <Button type="link" onClick={() => handleEdit(row)}>                編輯              </Button>              <Popconfirm title="確認(rèn)要?jiǎng)h除?" onConfirm={() => handleDelete(row)}>                <Button type="link">刪除</Button>              </Popconfirm>            </>          );        }      }] 

我們期望的是操作列也可以像表格的columns一樣通過配置來生成,而不是寫jsx??匆幌氯绾畏庋b呢?

  1. // 定義操作按鈕export interface IAction extends Omit<ButtonProps, 'onClick'> {  // 自定義按鈕渲染  render?: (row: anyindex: number) => React.ReactNode;  onClick?: (row: anyindex: number) => void;  // 是否有確認(rèn)提示  confirm?: boolean;  // 提示文字  confirmText?: boolean;  // 按鈕顯示文字  text: string;}// 定義表格列export interface IColumn<T = any> extends ColumnType<T> {  actions?: IAction[];}// 然后我們可以定義一個(gè)hooks,專門用來修改表格的columns,添加操作列const useActionButtons = (  columns: IColumn[],  actions: IAction[] | undefined): IColumn[] => {  return useMemo(() => {    if (!actions || actions.length === 0) {      return columns;    }    return [      ...columns,      {        align: 'center',        title: '操作',        key'__action',        dataIndex: '__action',        width: Math.max(120, actions.length * 85),        render(value: any, row: anyindex: number) {          return actions.map((item) => {            if (item.render) {              return item.render(row, index);            }            if(item.confirm) {              return <Popconfirm title={item.confirmText  || '確認(rèn)要?jiǎng)h除?'}                       onConfirm={() => item.onClick?.(row, index)}>                <Button type="link">{item.text}</Button>              </Popconfirm>            }            return (              <Button                {...item}                type="link"                key={item.text}                onClick={() => item.onClick?.(row, index)}              >                {item.text}              </Button>            );          });        }      }    ];  }, [columns, actions, actionFixed]);};// 最后我們對(duì)表格再做一個(gè)封裝const CustomTable: React.FC<ITableProps> = ({  actions,  columns,  ...props}) => {  const actionColumns = useActionColumns(columns,actions)  // 渲染表格} 

通過上面的封裝,我們?cè)偈褂帽砀竦臅r(shí)候,就可以這樣去寫

  1. const actions: IAction[] = [    {      text: '編輯',      onClick: handleModifyRecord,    },  ];return <CustomTable actions={actions} columns={columns}></CustomTable> 

 本文轉(zhuǎn)載自微信公眾號(hào)「前端有的玩」,可以通過以下二維碼關(guān)注。轉(zhuǎn)載本文請(qǐng)聯(lián)系前端有的玩公眾號(hào)。

 

責(zé)任編輯:武曉燕 來源: 前端有的玩
相關(guān)推薦

2022-03-04 08:17:53

PageRank網(wǎng)絡(luò)等級(jí)

2021-11-26 07:00:05

反轉(zhuǎn)整數(shù)數(shù)字

2022-01-02 08:43:46

Python

2022-02-07 11:01:23

ZooKeeper

2021-07-02 09:45:29

MySQL InnoDB數(shù)據(jù)

2021-07-06 08:59:18

抽象工廠模式

2023-01-03 08:31:54

Spring讀取器配置

2021-07-05 22:11:38

MySQL體系架構(gòu)

2021-05-11 08:54:59

建造者模式設(shè)計(jì)

2022-08-26 09:29:01

Kubernetes策略Master

2022-08-23 08:00:59

磁盤性能網(wǎng)絡(luò)

2023-11-28 08:29:31

Rust內(nèi)存布局

2021-09-28 08:59:30

復(fù)原IP地址

2021-10-14 10:22:19

逃逸JVM性能

2022-04-12 08:30:52

回調(diào)函數(shù)代碼調(diào)試

2021-10-27 09:59:35

存儲(chǔ)

2021-07-16 22:43:10

Go并發(fā)Golang

2023-03-13 21:38:08

TCP數(shù)據(jù)IP地址

2023-11-01 09:07:01

Spring裝配源碼

2022-10-20 07:39:26

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)