從零開始學習React:react中事件處理與柯里化

語言: CN / TW / HK

1. 事件處理

React 中元素也可接受、處理事件,但是在語法上有一點不同。

在React 中所有事件的命名採用的是小駝峰,而非原生 DOM 的純小寫,所有事件需要我們傳入一個函式,而非字串。

例如:

js const Button = () => { const handleClick = () => { console.log('click') } return <button onClick={handleClick}>click button</button> }

當事件的回撥函式比較簡單時,我們也可以簡寫箭頭匿名函式,例如:

js const Button = () => { return ( <button onClick={() => console.log('click')} > click button </button>) }

阻止預設行為

在React 中不能通過返回 false 來阻止預設行為,例如表單提交、a標籤跳轉。我們必須要通過顯式呼叫 preventDefault 函式,來阻止這些預設行為。

js const Link = () => { return <a href="http://www.baidu.com" onClick={(e) => e.preventDefault()} > link </a> }

合成事件

在 React 中幾乎所有的事件處理函式,都是一個 (event)=>void 函式,如果我們使用 typescript,可以清晰的看到每個事件對應的函式型別,React 自身也聲明瞭很多的事件與事件處理函式型別,例如滑鼠事件:MouseEvent<T = Element>MouseEventHandler<T = Element>,我們在使用時可以根據自己的喜歡,是定義函式型別還是定義引數型別,就像這樣:

js const Link = () => { const handleClick = (e: MouseEvent) => { e.preventDefault() console.log('click') } const handleMouseEnter:MouseEventHandler = (e) => { console.log('mouse enter') } return <a href="http://www.baidu.com" onMouseEnter={handleMouseEnter} onClick={handleClick} > link </a> }

在 React 中,所有事件都是 React 根據 W3C 規範定義的合成事件,所以我們完全不用擔心相容性問題,React 事件與原生事件不完全相同。

點選此處檢視合成事件文件

2. 柯里化

柯里化這個名稱對於 Android 開發可能有點陌生,因為我們一般使用 Java 開發,因為早期的 Java 不支援函數語言程式設計(FP),而柯里化是一個函數語言程式設計思想。

簡而言之是將一個多參函式變成單引數函式,舉個栗子:

js //柯里化後的單引數函式 function sumCurrying(a) { return (b) => { return (c) => { return a + b + c; }; }; } //普通的多引數函式 function sumNormal(a, b, c) { return a + b + c } console.log(sumCurrying(1)(2)(3)); console.log(sumNormal(1, 2, 3));

柯里化的本質,就是高階函式的一個特性:函式的返回值可以是一個函式。

上面的例子,似乎有點脫褲子放屁,看似毫無意義。但實際工程中,柯里化是一個非常實用的小 trick。最常用在事件處理需要傳入值的場景。

我們在上面說過了,React 中的事件回撥函式是有固定的函式型別的,幾乎都是 (event)=>void 函式。我們需要傳入一些引數給這個事件處理函式呢?

js const List = () => { const list = [ { id: 1, name: 'tom' }, { id: 2, name: 'jerry' }, { id: 3, name: 'jack' }, { id: 4, name: 'lily' }, ] const handleClick = (id: number) => { console.log(id) } return <ul> {list.map(item => <li onClick={() => handleClick(item.id)} key={item.id} > {item.name} </li> )} </ul> }

這看起來似乎很不優雅,我們已經聲明瞭 handle 函式,卻又不得不在事件處理函式中寫行內的箭頭函式,如何才能更加優雅的處理呢?

其實很簡單,我們只需要在原本的 handle 函式中,插入一個箭頭即可,就像這樣:

js //before const handleClick = (id: number) => { console.log(id) } //after const handleClick = (id: number) => (e:MouseEvent) => { console.log(id) }

然後我們的 onClick 事件回撥函式就可以改成 onClick={handleClick(item.id)} ,這樣看起來是不是就更加優雅了呢?

其實這種設計思想可以說是一說就透,只不過我現在告訴你,這種思想就叫做:柯里化

柯里化的目的

你可能會問我柯里化看起來只是讓我們的程式碼優雅了一點,在目前看來似乎沒有什麼本質上的變化。

但其實柯里化幫助我們實現了函式的一變多,我們用一個日誌輸出的函式作為例子:

js //原始函式 const log = (date, importance, message) => { alert(`[${date.getHours()}:${date.getMinutes()}] [${importance}] ${message}`); } //柯里化 const logCurry = (date) => (importance) => (message) => { alert(`[${date.getHours()}:${date.getMinutes()}] [${importance}] ${message}`); }

柯里化後,函式變成這樣呼叫:logCurry(new Date())("DEBUG")("some debug");

現在我們相當於擁有這些函式:

```js // logNow 會是帶有固定第一個引數的日誌的函式 let logNow = logCurry(new Date());

// 使用它 logNow("INFO", "message"); // [HH:mm] INFO message

// debugNow 會是帶有固定第一個引數與第二個引數的函式 let debugNow = logNow("DEBUG");

debugNow("message"); // [HH:mm] DEBUG message ```

看起來只是增加了幾個箭頭,實際上我們函式的靈活性大為增加。通過固定不同的引數,我們從一個函式宣告獲得了多個函式。

一個簡單的例子

``js const Form = () => { const [form, setForm] = React.useState({}); const update = (name) => (event) => { setForm({ ...form, [name]: event.target.value, }); } const handleSubmit = (event) => { event.preventDefault(); alert(${JSON.stringify(form)}`); } return (

柯里化表單

) }

const FormItem = ({ label, name, update }) => { return (

請輸入${label}} />
); }; ```