從零開始學習React:react中事件處理與柯里化
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 (