最新更新时间:2022年01月15日14:40:31
本文内容:一个父组件和两个子组件,三个组件都是函数式组件,子组件1需要调用子组件2的方法,同时子组件2需要调用子组件1的方法
技术方案分析
兄弟组件的方法互调
子组件1通过props调用父组件的方法,父组件通过ref2调用子组件2的方法
子组件2通过props调用父组件的方法,父组件通过ref1调用子组件1的方法
父组件调用子组件的方法
- 父组件通过ref获取子组件实例,然后再调用方法
- 子组件需要用到两个hooks,forwardRef和useImperativeHandle
- forwardRef,用户 转发refs到DOM组件 和 在高阶组件中转发refs
- useImperativeHandle可以让你在使用ref时自定义暴露给父组件的实例值。useImperativeHandle应当和forwardRef配合使用
获取通过Form.create()()创建的函数式组件的实例
- 通过子组件的wrappedComponentRef属性传递ref
获取被connect包裹的函数式组件的实例
注意:被withRouter、connect、Form.create等方法包裹的组件并不能抛出ref,需要使用forwardRef抛出子组件的ref
示例代码
父元素
import React, {useRef} from 'react'
import Child1 form '../child1';
import Child2 form '../child2';
function Parent(props){
const child1Ref = useRef(null);
const child2Ref = useRef(null);
const child1CallChild2 = ()=>{
console.log('父元素调用子元素2的方法')
child2Ref.current && child2Ref.parentCallChild2()
}
const child2CallChild1 = ()=>{
console.log('父元素调用子元素1的方法')
child1Ref.current && child1Ref.parentCallChild1()
}
return <div>
<Child1 wrappedComponentRef={child1Ref} child1CallChild2={child1CallChild2}/>
<Child2 ref={child2Ref} child2CallChild1={child2CallChild1} />
</div>
}
export default Parent
子元素1
- 通过Form.create()(Component)创建的函数式组件,对外暴露实例和方法
import React, {forwardRef, useImperativeHandle} from 'react'
import {Form} from 'antd'
function Child1(props, ref){
function parentCallChild1(){
console.log('父组件调用子组件1的方法')
}
function c1Callc2(){
console.log('兄弟元素通信 子组件1调用子组件2的方法')
props.child1CallChild2()
}
useImperativeHandle(ref,()=>({parentCallChild1}))
return <div onClick={this.c1Callc2}>
<Form></Form>
</div>
}
const FormComponentWrap = Form.create()(forwardRef(Child1))
export default FormComponentWrap
子元素2
- 通过connect()()创建的函数式组件,对外暴露实例和方法
import React, {forwardRef, useImperativeHandle} from 'react'
import { connect } from 'react-redux'
function Child2(props, ref){
function parentCallChild2(){
console.log('父组件调用子组件2的方法')
}
function c2Callc1(){
console.log('兄弟元素通信 子组件2调用子组件1的方法')
props.child2CallChild1()
}
useImperativeHandle(props.refInstance,()=>({parentCallChild2}))
return <div onClick={this.c2Callc1}>Child2</div>
}
const Component = connect(matStateToProps,mapDispatchToProps)(Child2)
export default forwardRef((props, ref)=> <Component {...props} refInstance={ref} /> )
connect方法,连接react组件与redux中的store,将数据和UI连接起来,如果不写mapStateToProps参数,UI 组件就不会订阅Store, Store 的更新不会引起 UI 组件的更新; matStateToProps是输入源,更新props; mapDispatchToProps是输出源,更新action;
老版本框架获取实例
this._childRef.current.getWrappedInstance()
@connect(matStateToProps, mapDispatchToProps, mergeProps, { withRef: true })
export default connect(matStateToProps, mapDispatchToProps, mergeProps, { forwardRef: true })
export default connect(matStateToProps, mapDispatchToProps, mergeProps, { withRef: true })
获取类组件的实例
import React, {} from 'react'
import Child from '../child'
class Parent extends React.Component{
constructor(){
}
callChildFunc(){
this.refs._childref.getWrappedInstance().getName();
this.refs._childref.wrappedInstance.getName();
}
render(){
return <Child ref={'_childref'} />
}
}
class Child extends React.Component{
getName(){
console.log('wan')
}
render(){
return <Child ref={'_childref'} />
}
}
withRouter的使用
高阶组件withRouter,是将一个组件包裹进Route里面,这个组件的this.props可以获取到react-router的三个对象history, location, match
对于不是通过路由跳转过来的组件,比如首页,如果想通过props获取路由信息,就需要用withRouter包裹这个组件
或者,比如通过this.props.history.push(’/1’)跳转页面,报错history是undefined,需要用withRouter包裹组件
import React,{Component} from 'react'
import {withRouter} from 'react-router-dom'
class App extends Component{
console.log(this.props);
constructor(props){
super(props);
props.history.listen((location) => {
switch(location.pathname){
case '/1/1' :
document.title = '第一页';
break;
case '/1/2' :
document.title = '第二页';
break;
default : break;
}
})
}
render(){}
}
export default withRouter(App);
参考资料
感谢阅读,欢迎评论^-^
|