組件的複用是 React 中非常重要的一個設計,使得界面佈局的重用十分便捷,我們可以根據自己的需要來自定義各種組件,這對於結構相似的 UI 構建是很友好的。
所以經常我們會出現如下的代碼結構(其中的 MyComponent 是自定義組件):
export default class Home extends Component{
...
render(){
<View>
<MyComponent/>
</View>
}
}
在這種結構中,Home 組件包含了 MyComponent 組件,形成父子組件關係,其中自然是少不了有數據的傳遞,甚至會需要調用互相的函數,那麼父子組件之間是如何傳值和函數調用的呢?
子組件獲取父組件的變量和函數
如果我們需要在 MyComponent 中獲取到 Home 頁面中的數據的話,可以使用通過 props 來傳遞。需要注意的是傳遞的數據,可以是普通的變量,也可以是函數。如例:
export default class Home extends Component {
constructor(props) {
super(props);
this.state = {
buttonText: '點擊調用父組件 Home 的函數',
}
}
onMyButtonPress = () => { //父組件的方法
ShowToast('Home 中的方法');
}
render() {
return (
<View>
<MyButton
//傳遞 text 變量
text={this.state.buttonText}
//傳遞 onMyButtonPress 作爲 子組件的 onPress 函數
onPress={this.onMyButtonPress}
/>
</View>
);
}
}
class MyButton extends Component {
render() {
//獲取傳遞的 onPress 函數和 text 變量
const {text, onPress} = this.props;
return (
<TouchableOpacity
style={styles.button}
//實際調用的是父組件傳過來的 onPress 函數
onPress={onPress}
>
<Text>{text}</Text>
</TouchableOpacity>
);
}
}
(代碼重點表現 props 傳遞,省略了導入和樣式部分)
效果:
上部分的 MyButton, 按鈕中的文字和點擊事件都是外部父組件傳入的,本身不持有任何靜態變量。在此基礎上,我們可以加上自定義的樣式或者其他元素,就成爲一個可以複用的按鈕組件。
父組件獲取子組件的變量和函數
如果我們要在外部的父組件中獲取到子組件的值的話,那就需要使用到 ref 了。如果對 ref 不太熟,可以參見 [Refs and the DOM]
。
這裏,你可以簡單理解爲綁定 ref 就可以獲取到當前的 DOM 元素節點,從而獲取它的變量和函數。
怎麼操作呢?如下:
export default class Home extends Component {
constructor(props) {
super(props);
this.state = {
buttonText: '點擊調用父組件 Home 的函數',
}
//創建 MytextInput 的 ref
this.myButton = React.createRef();
}
//輸出子組件中的變量,調用子組件方法
onMyButtonPress = () => {
//在調試控制檯輸出 輸入框中輸入的值
console.log('輸入的值:' + this.mytextInput.current.state.inputText);
//調用 MyTextInput 內部的方法 innerMethod
this.mytextInput.current.innerMethod();
//將 MyTextInput 輸入框的內容清除掉 (clear是TextInput組件本身的方法)
this.mytextInput.current.textInputRef.current.clear();
}
render() {
return (
<View>
<MyTextInput
ref={this.mytextInput}
/>
<TouchableOpacity
onPress={this.onMyButtonPress}
>
<Text>確認</Text>
</TouchableOpacity>
</View>
);
}
}
//自定義組件 MyTextInput
class MyTextInput extends Component {
constructor(props) {
super(props);
this.state = {
inputText: ''
}
this.textInputRef = React.createRef();
}
// MyTextInput 的普通方法
innerMethod = () => {
ShowToast('MyTextInput 內部方法');
}
render() {
return (
<View>
<TextInput
placeholder={'輸入內容後點擊按鈕'}
style={styles.input}
onChangeText={text => this.setState({inputText: text})}
ref={this.textInputRef}
/>
</View>
);
}
}
效果:
可見,子組件中的 TextInput 的輸入內容,可以在父組件中獲取並打印輸出出來, innerMethod 是定義在子組件中的函數,也可以在父組件中得以調用。而子組件的 TextInput 組件的 clear 方法(清除輸入框的內容),依然能被父組件正確調用。
至此,我們已經在父組件中獲取到子組件的變量和函數了。
回調函數
如果只是需要傳遞數值到父組件,不需要在父組件中調用子組件的方法,並且觸發的動作事件是在子組件中發生的話(比如子組件點擊之後需要將一個變量暴露到父組件中,在父組件的多個地方用於顯示),那可以考慮使用回調函數。
例如:在子組件中有一個輸入框,輸入完成後,點擊軟鍵盤的完成提交,暴露給父組件。
子組件中:
//調用父組件的函數,傳入值
onSubmitPress = () => {
const {submitText} = this.props;
//調用父組件傳過來的函數,傳入 值(輸入的文本)
submitText(this.state.inputText);
}
render() {
return (
<View>
<TextInput
onSubmitEditing={this.onSubmitPress}
...
/>
</View>
);
}
父組件中使用:
<MyTextInput
//接收傳過來的值, setState
submitText={text => this.setState({resultText: text})}
ref={this.myButton}
/>
這樣,我們在輸入完成後,提交方法,調用父組件傳過來的 submitText 函數,並傳入的輸入的文本,父組件的方法接收到再 setState 就獲得了這個變量。至此,父組件就獲取到了子組件中的變量。
在父組件傳值到子組件的過程中,如果層級較深,應該考慮使用狀態管理工具。子組件傳值到父組件,除了以上方法,還可以通過 useRef,useImperativeHandle 等 Hook API 達到訪問子組件數據的目的。這將在後面的 Hook 相關章節介紹,敬請期待。