概述
React典型的数据流自上而下的,父组件通过props可以直接传递数据给子组件。但有些时候我们可能会想要在父组件中拿到子组件中的一些方法,这个时候我们可以通过refs获取到子组件的实例,进而实现子组件向父组件的通信。
最近在学习typescript,所以以下示例都是用tsx写的。
Class Component
在类组件中,通过ref引用子组件后就可以调用类中自定义的方法,这是类的特性所决定的。
- React.creactRef
引用保存在ref.current属性中
class Children extends Component {
public state = {
name: 'Children',
};
public print() {
console.log('print');
}
public render() {
return <div>{this.state.name}</div>;
}
}
class Father extends Component {
public ref = React.createRef<any>();
public handleClick() {
this.ref.current.print();
}
public render() {
return (
<>
<button onClick={() => this.handleClick()}>click</button>
<Children ref={this.ref} />
</>
);
}
}
- 回调Refs
此处有两个示例,其中textInput是对element元素的引用, inputRef是对Input组件的引用
class Input extends Component {
textInput: HTMLElement | null = null;
componentDidMount() {
this.textInput?.focus();
}
public showInfo = () => {
console.log('this is callback ref');
};
render() {
return (
<div>
<input
ref={(element) => {
this.textInput = element;
}}
/>
<button onClick={() => this.handleClick()}>click</button>
</div>
);
}
private handleClick = () => {
console.log(this.textInput);
};
}
class TopInput extends Component {
public inputRef: Input | null = null;
public executeChildren = () => {
this.inputRef?.showInfo();
};
render() {
return (
<div>
<Input
ref={(element) => {
this.inputRef = element;
}}
/>
<button onClick={() => this.executeChildren()}>click_2</button>
</div>
);
}
}
- 使用props自定义ref属性
interface SubProps {
onRef: (ref: any) => void;
}
interface SubState {
name: string;
}
class Sub extends Component<SubProps, SubState> {
name = 'Sub';
onCallback() {
console.log('callback');
}
componentDidMount() {
this.props.onRef(this);
}
render() {
return <div />;
}
}
class Super extends Component {
customRef: Sub | null = null;
handleClick() {
this.customRef?.onCallback();
}
render() {
return (
<div>
<button onClick={() => this.handleClick()}>onRef</button>
<Sub
onRef={(node: Sub | null) => {
this.customRef = node;
}}
/>
</div>
);
}
}
Function Component
通过useImperativeHandle hook结合forwardRef可以得到子组件暴露出来的实例
type Handle = {
handleChange: () => void;
print: () => void;
open: boolean;
};
const Dog = forwardRef((props, ref: React.ForwardedRef<Handle>) => {
const [open, setOpen] = useState(false);
useImperativeHandle(
ref,
() => ({
handleChange: () => {
setOpen(!open);
},
print: () => {
console.log('useImperativeHandle');
},
open: open,
}),
[open],
);
console.log(open);
return <div>forwardRef {open && <span>open</span>}</div>;
});
function Animal() {
const dogRef = useRef<Handle>(null);
const handleClick = () => {
dogRef.current?.print();
dogRef.current?.handleChange();
// 此处拿到的open是还未变化的值
console.log(dogRef.current?.open);
};
return (
<div>
<Dog ref={dogRef} />
<button onClick={handleClick}>dogClick</button>
</div>
);
}
总结
对于类组件,核心是拿到对子组件的引用。
而函数组件,关键在于useImperativeHandle的使用。
当然对于嵌套更深的祖孙组件,也可以通过window.dispatchEvent()事件监听的方式通知目标组件。
以上就是我的一些想法总结,欢迎大家指正补充。