React 之【父子元件傳值 通訊】
在日常開發過程中,我們經常需要使用ref,來做一些邏輯處理。對於當前元件來說,ref的使用比較方便,而對於跨元件使用,尤其是現在hooks的廣泛運用和以前的class的交叉使用,有時候會讓我們使用上出現一些問題。比如在父元件中引入子元件,把ref傳入到函式子元件中,會一直為空的問題。
class父元件與class子元件及函式式子元件利用ref進行通訊
父元件-class類元件
//測試呼叫子元件方法 class Home extends React.Component { childRef = null; render() { return ( <div> <Test ref={ref => this.childRef = ref} /> <Button onClick={() => { console.log(this.childRef) }}> </Button> </div> ) } }
子元件-class元件
class ForwardRef extends React.Component {
test = () => {
console.log('我是ForwardRef元件的test方法')
}
render(){
return (
<div>
ForwardRef元件
</div>
)
}
}
export default ForwardRef;
子元件-函式元件
const ForwardRef = (props) => { console.log(props) return ( <div> ForwardRef元件 </div> ) } export default ForwardRef;
我們分別在控制檯輸出一下props
class子元件:
函式子元件:
如上可知,在class元件中傳遞ref給函式元件,接收到的都是undefined,而class元件是能接收父元件傳過來的ref的。那怎麼能把ref從class父元件傳遞給函式子元件呢?
React官網中提到 useImperativeHandle
可以讓你在使用ref
時自定義暴露給父元件的例項值。在大多數情況下,應當避免使用 ref 這樣的命令式程式碼。useImperativeHandle
應當與forwardRef
一起使用:
function FancyInput(props, ref) { const inputRef = useRef(); useImperativeHandle(ref, () => ({ focus: () => { inputRef.current.focus(); } })); return <input ref={inputRef} ... />; } FancyInput = forwardRef(FancyInput);
在本例中,渲染<FancyInput ref={inputRef} />
的父元件可以呼叫inputRef.current.focus()
。
回到我們的例子:
class Home extends React.Component {
childRef = null;
render() {
return (
<div>
<Test ref={ref => this.childRef = ref} />
<Button onClick={() => {
if(this.childRef){
console.log(this.childRef.test())
}
}}>
</Button>
</div>
)
}
}
export default connect()(Home);
import React, {
useImperativeHandle,
forwardRef
} from 'react';
const ForwardRef = forwardRef((props, ref) => {
console.log(props)
useImperativeHandle(ref, () => ({
test,
}));
const test = () => {
console.log('我是ForwardRef元件的test方法')
}
return (
<div>
ForwardRef元件
</div>
)
})
export default ForwardRef;
可以看到可以獲取到子元件的方法了.
但是最近在做一個需求時,我又發現上述方法不管用了,這是為什麼呢?
在react專案中,我們經常會用到HOC元件,比如為了頁面出錯而不至於全域性崩潰的error包裝元件,每個元件出錯只把當前元件出錯顯示,而整體頁面不至於崩潰。比如側邊欄、彈窗、tab切換等開啟的時候,如果當前元件崩潰,只顯示列印當前出錯的元件顯示部位,使用者體驗不會太差。還有我們經常使用的全域性資料管理,比如dva的connect包裝元件,使得元件可以使用dva的全域性狀態。
當前父元件程式碼如下:
import React from 'react';
import { connect } from 'dva';
import { Button } from 'antd';
import Test from './component/forwardRef';
class Home extends React.Component {
childRef = null;
render() {
return (
<div>
<Test ref={ref => this.childRef = ref} />
<Button onClick={() => {
if(this.childRef){
console.log(this.childRef)
}
}}>
</Button>
</div>
)
}
}
export default connect()(Home);
子元件為函式元件,被connect包裝。
import React, {
} from 'react';
import { connect } from 'dva';
const ForwardRef = (props) => {
console.log(props)
const test = () => {
console.log('我是ForwardRef元件的test方法')
}
return (
<div>
ForwardRef元件
</div>
)
}
const ForwardRefWrap = connect()(ForwardRef)
export default ForwardRefWrap;
此時我們在父元件通過ref呼叫子元件的時候,會發現列印的其實是子元件被connect包裝的HOC元件,無法獲取我們真實想要的子元件,更別說呼叫子元件的方法了。
怎麼解決?
父元件不做修改,子元件改成下面這種,因為forwardRef函式是作為把父元件的ref傳遞給子元件中,並且進行包裝,props和ref進行分開傳遞,所以應該是connect進行包裝,forwardRef在最外層進行包裝,才能把父元件的ref傳入到子元件中。
import React, {
useImperativeHandle,
forwardRef
} from 'react';
import { connect } from 'dva';
const ForwardRef = (props) => {
console.log(props)
const {refInstance} = props;
useImperativeHandle(refInstance, () => ({
test,
}));
const test = () => {
console.log('我是ForwardRef元件的test方法')
}
return (
<div>
ForwardRef元件
</div>
)
}
const ForwardRefWrap = connect()(ForwardRef)
export default forwardRef((props, ref) => <ForwardRefWrap {...props} refInstance={ref} />);
完美解決
函式式父元件向函式式子元件利用ref傳值
父元件
import React, {
useRef
} from 'react';
const ForwardRef = (props) => {
const dataInfo = useRef()
const handleSubmit = () => {
const dataInfoSource = dataInfo?.current?.getVal()
//測試拿到子元件狀態及方法
console.log(dataInfoSource)
};
return (
<div>
<Child cRef={dataInfo} />
<Button onClick={handleSubmit}>
</div>
)
}
export default ForwardRef;
子元件
import React, {
useImperativeHandle,
forwardRef
} from 'react';
import { connect } from 'dva';
const indexInfoCard = (props) => {
const {cRef} = props;
useImperativeHandle(cRef, () => ({
test,
}));
const test = () => {
console.log('我是ForwardRef元件的test方法')
}
return (
<div>
ForwardRef元件
</div>
)
}
const ForwardRefWrap = connect()(ForwardRef)
export default forwardRef((props, ref) => <ForwardRefWrap {...props} refInstance={ref} />);
完美解決,總結也是為了加深記憶,因為剛接觸React沒多久,還需要沉澱。