文章目錄
- 1. react事件為何需要bind this
- (1)箭頭函數
- (2)bind改變this指向
- (3)構造函數中使用箭頭函數綁定this
- 2. react事件和dom事件的區別
- 3. react事件中的event參數
- 4. react事件中的自定義參數
- 5. 自定義參數和event參數共存
- 6. 受控組件和非受控組件
- 7. props實現父子組件通信
- 1. 父傳子
- 2. 子傳父
- 8.setState第一個參數的兩種形式
- 9. setState的第二個參數
- 10. setState是同步的還是異步的
- (1). 異步更新的情況:
- (2). 同步更新的情況:
- 11. react的生命周期
- 12. 函數組件和類組件的區別
1. react事件為何需要bind this
React事件需要綁定this,是因為React中的事件處理函數是普通函數,并不是類的方法。在JavaScript中,普通函數在被調用時,其this關鍵字會指向調用它的對象。而在React中,事件處理函數并不是在其所屬的組件實例的上下文中被調用的,所以如果不綁定this,this將會指向undefined或者全局對象。
為了確保事件處理函數中的this能夠指向組件實例,需要將事件處理函數綁定到組件實例。方法有很多種,常見的有在構造函數中使用bind綁定this,在事件處理函數中使用箭頭函數,或者使用類屬性的箭頭函數來定義事件處理函數。這樣做可以確保事件處理函數中的this指向組件實例,可以正確地使用組件的狀態和方法。
舉例來說明,假設在一個React組件中有一個按鈕,點擊按鈕時需要調用組件實例的方法并傳入參數:
class MyComponent extends React.Component {constructor(props) {super(props);this.state = {count: 0};this.handleClick = this.handleClick.bind(this);}handleClick() {this.setState({ count: this.state.count + 1 });}render() {return (<div><p>Count: {this.state.count}</p><button onClick={this.handleClick}>Increment</button></div>);}
}
在上面的例子中,由于React事件處理函數默認是在全局作用域下執行的,若不綁定this,點擊按鈕時會出現錯誤。通過在構造函數中使用.bind(this)
綁定this,確保了在事件處理函數中可以正確訪問組件實例的屬性及方法。
解決方法
(1)箭頭函數
一種常見的解決辦法是使用箭頭函數,在箭頭函數中,this會指向箭頭函數所在的上下文,而不受外部函數調用的影響,因此可以解決React事件處理函數中this指向錯誤的問題。例如:
class MyComponent extends React.Component {handleClick = () => {console.log(this); // this指向MyComponent實例}render() {return <button onClick={this.handleClick}>Click me</button>;}
}
(2)bind改變this指向
另一種解決辦法是顯式綁定this,在事件處理函數中使用bind方法將this指定為當前實例。例如:
class MyComponent extends React.Component {constructor(props) {super(props);this.handleClick = this.handleClick.bind(this);}handleClick() {console.log(this); // this指向MyComponent實例}render() {return <button onClick={this.handleClick}>Click me</button>;}
}
(3)構造函數中使用箭頭函數綁定this
還有一種解決辦法是在構造函數中使用箭頭函數綁定this,這種方法與第二種方法類似,也是通過綁定this來解決問題。例如:
class MyComponent extends React.Component {constructor(props) {super(props);this.handleClick = () => {console.log(this); // this指向MyComponent實例}}render() {return <button onClick={this.handleClick}>Click me</button>;}
}
這些都是常見的解決React事件處理函數中this指向問題的方法,可以根據個人習慣和項目需求來選擇使用哪種方法。
2. react事件和dom事件的區別
React事件和DOM事件的主要區別在于它們的處理方式和綁定方法。
React事件使用了合成事件(SyntheticEvent
),這是一個在React中封裝的事件系統,它屏蔽了不同瀏覽器之間的事件差異,提供了統一的事件處理方式。React事件綁定可以直接在JSX中使用onXxx屬性來綁定事件處理函數。
DOM事件是瀏覽器原生的事件系統,通過addEventListener方法來綁定事件處理函數。DOM事件處理中需要特別注意跨瀏覽器的兼容性,因為不同瀏覽器可能會有不同的事件名稱或參數。
舉例來說,對于一個按鈕點擊事件的處理:
React事件:
class MyComponent extends React.Component {handleClick = () => {alert('Button clicked!');}render() {return <button onClick={this.handleClick}>Click me</button>;}
}
DOM事件:
document.getElementById('myButton').addEventListener('click', () => {alert('Button clicked!');
});
<div id="myButton">Click me</div>
在React中,我們可以直接在JSX中使用onClick屬性來綁定點擊事件處理函數,而在DOM中則需要通過addEventListener方法來實現相同的效果。React的事件處理方式更加簡潔和易用,同時也提供了更好的跨瀏覽器兼容性。
3. react事件中的event參數
React事件中的event參數并不是瀏覽器原生的event,而是由React封裝而成的合成事件(SyntheticEvent
)。合成事件是React對瀏覽器原生事件的一個跨瀏覽器封裝,可以提供一致的API和行為。
區別:
- 合成事件提供了一致的跨瀏覽器API,不需要手動處理瀏覽器兼容性問題。
- 合成事件可以使用一些額外的功能,比如可以調用preventDefault()來阻止默認行為,調用stopPropagation()來停止事件冒泡。
- 合成事件是一個以JavaScript對象為基礎的事件系統,相比原生事件更輕量級,性能更高。
舉例說明:
function handleClick(event) {event.preventDefault(); // 阻止默認行為console.log(event.target); // 獲取觸發事件的元素console.log(event.pageX, event.pageY); // 獲取鼠標點擊位置
}<button onClick={handleClick}>Click me</button>
在上面的例子中,handleClick函數接受一個合成事件作為參數,可以通過該事件對象來阻止默認行為、獲取觸發事件的元素以及鼠標點擊的位置等信息。
4. react事件中的自定義參數
在React中,可以通過箭頭函數的方式將自定義參數傳遞給事件處理程序。例如,如果想要在點擊按鈕時傳遞一個特定的值,可以像下面這樣編寫代碼:
import React from 'react';class MyComponent extends React.Component {handleClick = (value) => {console.log(`Clicked with value: ${value}`);}render() {return (<div><button onClick={() => this.handleClick('customValue')}>Click me</button></div>);}
}export default MyComponent;
在上面的代碼中,當點擊按鈕時,會觸發 handleClick 方法并輸出 “Clicked with value: customValue”。這樣就可以很方便地傳遞自定義參數給React事件處理程序。
5. 自定義參數和event參數共存
如果需要在React事件中傳遞自定義參數并且還需要使用event參數,可以使用箭頭函數來定義事件函數。在箭頭函數中,可以手動傳遞自定義參數,并且在調用事件處理函數時,React會自動傳遞event參數。
舉個例子,假設有一個按鈕組件,點擊按鈕時需要傳遞一個自定義參數id
并且需要獲取event對象:
import React from 'react';class CustomButton extends React.Component {handleClick = (id, event) => {console.log('Clicked ID:', id);console.log('Event:', event);};render() {const id = 123; // 自定義參數return (<button onClick={(event) => this.handleClick(id, event)}>Click Me</button>);}
}export default CustomButton;
在上面的例子中,當點擊按鈕時,會調用handleClick
事件處理函數,并傳遞自定義參數id
和React自動傳遞的event
參數。在handleClick
函數中,可以分別打印出傳遞的自定義參數和event對象。
6. 受控組件和非受控組件
在React中,受控組件和非受控組件是以組件的狀態是否由React來管理為區別的兩種組件。
- 受控組件(Controlled Components):在受控組件中,組件的狀態(例如輸入框的值)由React的state來管理,并通過props傳遞到組件中。當用戶與組件交互時,組件的狀態會隨著用戶輸入的變化而更新。受控組件可以實現數據的雙向綁定,即組件中的狀態變化會同時影響到視圖,以及用戶輸入的變化也會實時更新到狀態中。
舉例:
class ControlledComponent extends React.Component {constructor(props) {super(props);this.state = { value: '' };}handleChange = (event) => {this.setState({ value: event.target.value });};render() {return (<input type="text" value={this.state.value} onChange={this.handleChange} />);}
}
React的受控組件其實所實現的效果和Vue的v-model是一樣的
Vue的v-model指的是直接在表單元素上使用v-model指令來實現數據的雙向綁定。例如,在Vue中,一個input元素可以這樣定義:
<template><input type="text" v-model="value" />
</template><script>
export default {data() {return {value: ''};}
}
</script>
總的來說,React的受控組件和Vue的v-model都是通過數據綁定來實現雙向綁定的效果,只是具體的實現方式和語法有所不同。
- 非受控組件(Uncontrolled Components):在非受控組件中,組件的狀態不由React的state管理,而是由DOM本身來管理。通過ref屬性可以獲取到DOM節點,并直接操作DOM節點的值。非受控組件通常用于一些簡單組件或與外部庫集成時的情況。
舉例:
class UncontrolledComponent extends React.Component {constructor(props) {super(props);this.inputRef = React.createRef();}handleClick = () => {alert(this.inputRef.current.value);};render() {return (<div><input type="text" ref={this.inputRef} /><button onClick={this.handleClick}>Show Value</button></div>);}
}
7. props實現父子組件通信
在React中,父組件可以通過props將數據傳遞給子組件,從而實現父子組件之間的通信。而子組件可以通過回調函數將數據傳遞給父組件,實現子傳父的通信。
1. 父傳子
父組件可以通過props將數據傳遞給子組件。例如,父組件可以將一個名為data的變量傳遞給子組件:
// ParentComponent.js
import React from 'react';
import ChildComponent from './ChildComponent';class ParentComponent extends React.Component {render() {const data = 'Hello from ParentComponent';return (<ChildComponent data={data} />);}
}export default ParentComponent;
// ChildComponent.js
import React from 'react';class ChildComponent extends React.Component {render() {return (<div>{this.props.data}</div>);}
}export default ChildComponent;
在上面的例子中,父組件ParentComponent通過props將data傳遞給子組件ChildComponent,子組件通過this.props.data獲取數據并進行渲染。
2. 子傳父
子組件可以通過回調函數將數據傳遞給父組件。例如,子組件可以傳遞一個名為handleClick的回調函數給父組件,當子組件中的按鈕被點擊時,通過這個回調函數將數據傳遞給父組件:
// ParentComponent.js
import React from 'react';
import ChildComponent from './ChildComponent';class ParentComponent extends React.Component {constructor(props) {super(props);this.state = {childData: ''};}handleChildData = (data) => {this.setState({ childData: data });}render() {return (<div><ChildComponent onChildData={this.handleChildData} /><p>Child Data: {this.state.childData}</p></div>);}
}export default ParentComponent;
// ChildComponent.js
import React from 'react';class ChildComponent extends React.Component {sendDataToParent = () => {const data = 'Hello from ChildComponent';this.props.onChildData(data);}render() {return (<div><button onClick={this.sendDataToParent}>Send Data</button></div>);}
}export default ChildComponent;
在上面的例子中,子組件ChildComponent通過props將handleChildData這個回調函數傳遞給父組件ParentComponent,當子組件中的按鈕被點擊時,會調用這個回調函數并傳遞數據,父組件會通過這個回調函數接收子組件傳遞的數據并更新狀態。
8.setState第一個參數的兩種形式
在React中,使用setState更新state中的某個值時,可以傳入一個對象或者一個函數兩種方式。
傳入對象的形式更新state的值時,React會將傳入的對象與原來的state進行淺合并(shallow merge),只更新傳入對象中的屬性,不會對其他屬性進行改變。
例如:
// 傳入對象
this.setState({count: this.state.count + 1});
傳入函數的形式更新state的值時,接受兩個參數:prevState(代表上一個state的值)和props(代表組件的props)。函數會返回一個新的state對象,React會使用此新的state對象更新state。
例如:
// 傳入函數
this.setState((prevState, props) => {return {count: prevState.count + 1};
});
區別:
- 當使用對象形式更新state時,由于React的批處理機制,如果多次調用setState并傳入對象,React會將這些傳入的對象合并成一個對象后再更新state,因此可能導致不可預測的結果。
- 當使用函數形式更新state時,可以保證每次更新都是基于最新的state值進行的計算,避免了對象形式的不可預測問題。此外,函數形式的更新也可以保證在異步更新時,獲取到最新的state值。
為什么傳入對象會合并呢? *
這是因為React通過批處理機制來提高性能。當調用setState時,React并不會立即更新state,而是將所有的setState操作放入隊列中,然后在隊列中的所有操作執行完成后再統一更新state。在這個過程中,React會將所有傳入的對象合并成一個對象,然后再更新state。這樣做可以減少不必要的渲染次數,提高性能。如果需要在setState之后使用新的state值進行計算或者操作,可以使用回調函數的方式傳入setState,這樣可以確保在state更新之后再執行相關操作。
9. setState的第二個參數
在React中,調用setState
方法的第二個參數是一個回調函數,它會在setState
方法執行完并且組件重新渲染之后被調用。
使用setState
的第二個參數主要用于在setState
完成后執行一些需要在更新狀態之后立即進行的操作。這種情況通常是因為setState
是一個異步方法,所以如果需要確保在setState
完成后執行一些操作,可以使用第二個參數的回調函數。
舉一個例子:
import React, { Component } from 'react';class ExampleComponent extends Component {constructor(props) {super(props);this.state = {count: 0};}handleClick = () => {this.setState({ count: this.state.count + 1 }, () => {console.log('Count updated:', this.state.count);});}render() {return (<div><p>Count: {this.state.count}</p><button onClick={this.handleClick}>Increment Count</button></div>);}
}export default ExampleComponent;
在上面的例子中,當按鈕被點擊時,handleClick
方法會調用setState
來增加count
的值。第二個參數是一個回調函數,用于在更新count
之后立即輸出新的count
值。這樣可以確保我們在狀態更新完成后執行額外的邏輯。
與之類似,在Vue中,$nextTick
函數也可以用于在DOM更新之后執行一些操作。$nextTick
會在下次DOM更新循環結束后執行傳入的回調函數。
舉例來說,在React中可以這樣使用setState的第二個參數:
this.setState({ count: this.state.count + 1 }, () => {console.log('State updated');
});
在Vue中,則可以這樣使用$nextTick函數:
this.count = this.count + 1;
this.$nextTick(() => {console.log('DOM updated');
});
總的來說,React中的setState的第二個參數和Vue中的$nextTick函數都可以用于在狀態更新完成并且DOM重新渲染后執行一些操作,但是具體用法和實現機制有一些細微差別。
10. setState是同步的還是異步的
在React中,setState的更新有時候是同步的,有時候是異步的。具體來說:
-
當在組件生命周期方法(如
componentDidMount、componentDidUpdate
)或者事件處理函數中調用setState時,更新是異步的。這是因為React會將setState調用放入更新隊列中,然后一次性執行所有更新,以提高性能。 -
當在原生事件處理函數(如
setTimeout、addEventListener
)中調用setState時,更新是同步的。這是因為原生事件處理函數是在React的更新之外執行的,React無法控制更新的時機。
總的來說,大多數情況下setState的更新是異步的,但在某些特定情況下會是同步的。為了避免不必要的問題,最好遵循React官方的建議,不要直接依賴于setState的同步或異步特性,而是使用回調函數或者生命周期方法來處理更新后的邏輯
(1). 異步更新的情況:
import React, { Component } from 'react';class Example extends Component {constructor(props) {super(props);this.state = {count: 0};}componentDidMount() {this.setState({ count: this.state.count + 1 });console.log(this.state.count); // 輸出0,因為更新是異步的}render() {return (<div>{this.state.count}</div>);}
}export default Example;
(2). 同步更新的情況:
import React, { Component } from 'react';class Example extends Component {constructor(props) {super(props);this.state = {count: 0};}handleClick = () => {setTimeout(() => {this.setState({ count: this.state.count + 1 });console.log(this.state.count); // 輸出1,因為更新是同步的}, 0);}render() {return (<button onClick={this.handleClick}>Click me</button>);}
}export default Example;
在第一個例子中,componentDidMount中調用setState進行更新是異步的,所以在輸出狀態之前打印狀態會得到之前的狀態。在第二個例子中,通過setTimeout中調用setState進行更新是同步的,因為setTimeout是在React更新之外執行的,所以可以立即看到狀態的變化。
*
注意
這一特性是在react版本小于18以前,在18版本之后,都是異步的。 Automatic Batching 自動批處理
11. react的生命周期
React組件的生命周期可以分為三個階段:掛載階段、更新階段和卸載階段。每個階段有不同的生命周期方法可以供開發者重寫并在組件生命周期中做一些特定的操作。
- 掛載階段:
- constructor:組件被實例化時調用,用于初始化組件的狀態。
- getDerivedStateFromProps:在props發生變化時調用,用于更新組件的狀態。
- render:渲染界面的方法,必須實現。
- componentDidMount:組件掛載完成后調用,可以進行一些異步操作、數據請求等操作。
- 更新階段:
- getDerivedStateFromProps:在props發生變化時調用,用于更新組件的狀態。
- shouldComponentUpdate:在組件更新之前調用,可以控制組件是否需要重新渲染。
- render:渲染界面的方法,必須實現。
- getSnapshotBeforeUpdate:在組件更新之后調用,可以獲取更新前的DOM狀態信息。
- componentDidUpdate:組件更新完成后調用,可以進行一些DOM操作、數據請求等操作。
- 卸載階段:
- componentWillUnmount:組件將被卸載時調用,可以進行一些清理工作,如清除定時器、取消訂閱等操作。
在React版本16.4之后,更新了組件生命周期方法,引入了新的生命周期方法getDerivedStateFromProps
、getSnapshotBeforeUpdate
,取代了之前的componentWillReceiveProps
和componentWillUpdate
方法。需要根據實際情況選擇合適的生命周期方法來實現組件的功能。
12. 函數組件和類組件的區別
函數組件和類組件是 React 中兩種常見的組件類型,它們之間的區別如下:
-
語法:函數組件是以函數的形式來定義的,而類組件是以 ES6 類的形式來定義的。
-
狀態管理:在函數組件中,可以使用 React Hooks 來管理狀態,如 useState 和 useEffect。而在類組件中,使用 this.state 和 this.setState 來管理組件的狀態。
-
生命周期方法:在類組件中,可以使用一系列的生命周期方法,如 componentDidMount、componentDidUpdate、componentWillUnmount 等來處理組件的生命周期,而函數組件中使用 useEffect 來模擬這些生命周期方法的功能。
-
this 關鍵字:在類組件中,需要使用 this 關鍵字來訪問組件的屬性和方法,而函數組件中沒有 this 關鍵字。
總的來說,隨著 React Hooks 的引入,函數組件已經可以完全替代類組件,并且函數組件更加簡潔、易于理解和維護。因此,在新的 React 項目中,推薦使用函數組件來編寫組件。
大家可以看看這篇react中的函數組件和類組件