从父类调用子类方法
我有两个组件:
- 父组件
- 子组件
我试图从父组件调用子组件的方法,我尝试了这种方式但没有得到结果:
class Parent extends Component {
render() {
return (
<Child>
<button onClick={Child.getAlert()}>Click</button>
</Child>
);
}
}
class Child extends Component {
getAlert() {
alert('clicked');
}
render() {
return (
<h1 ref="hello">Hello</h1>
);
}
}
有没有办法从父组件调用子组件的方法?
注意:子组件和父组件位于两个不同的文件中。
首先,我要说的是,这通常
不是
在 React 领域中处理事情的方式。通常您想要做的是将功能传递给 props 中的子组件,并在事件中传递来自子组件的通知(或者更好的是:
dispatch
)。
但是如果您 必须 在子组件上公开命令式方法,则可以使用 refs 。请记住,这是一个逃生舱,通常表示有更好的设计可用。
Previously, refs were only supported for Class-based components. With the advent of React Hooks , that's no longer the case
带有 Hooks 的现代 React (
v16.8+
)
const { forwardRef, useRef, useImperativeHandle } = React;
// We need to wrap component in `forwardRef` in order to gain
// access to the ref object that is assigned using the `ref` prop.
// This ref is passed as the second parameter to the function component.
const Child = forwardRef((props, ref) => {
// The component instance will be extended
// with whatever you return from the callback passed
// as the second argument
useImperativeHandle(ref, () => ({
getAlert() {
alert("getAlert from Child");
}
}));
return <h1>Hi</h1>;
});
const Parent = () => {
// In order to gain access to the child component instance,
// you need to assign it to a `ref`, so we call `useRef()` to get one
const childRef = useRef();
return (
<div>
<Child ref={childRef} />
<button onClick={() => childRef.current.getAlert()}>Click</button>
</div>
);
};
ReactDOM.render(
<Parent />,
document.getElementById('root')
);
<script src="https://unpkg.com/react@16/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js" crossorigin></script>
<div id="root"></div>
useImperativeHandle()
的文档位于
此处
:
useImperativeHandle
customizes the instance value that is exposed to parent components when usingref
.
使用类组件的旧版 API (
>=
[email protected]
)
const { Component } = React;
class Parent extends Component {
constructor(props) {
super(props);
this.child = React.createRef();
}
onClick = () => {
this.child.current.getAlert();
};
render() {
return (
<div>
<Child ref={this.child} />
<button onClick={this.onClick}>Click</button>
</div>
);
}
}
class Child extends Component {
getAlert() {
alert('getAlert from Child');
}
render() {
return <h1>Hello</h1>;
}
}
ReactDOM.render(<Parent />, document.getElementById('root'));
<script src="https://unpkg.com/react@16/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js" crossorigin></script>
<div id="root"></div>
回调 Ref API
回调样式的 refs 是实现此目的的另一种方法,尽管在现代 React 中并不那么常见:
const { Component } = React;
const { render } = ReactDOM;
class Parent extends Component {
render() {
return (
<div>
<Child ref={instance => { this.child = instance; }} />
<button onClick={() => { this.child.getAlert(); }}>Click</button>
</div>
);
}
}
class Child extends Component {
getAlert() {
alert('clicked');
}
render() {
return (
<h1>Hello</h1>
);
}
}
render(
<Parent />,
document.getElementById('app')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>
您可以在此处使用另一种模式:
class Parent extends Component {
render() {
return (
<div>
<Child setClick={click => this.clickChild = click}/>
<button onClick={() => this.clickChild()}>Click</button>
</div>
);
}
}
class Child extends Component {
constructor(props) {
super(props);
this.getAlert = this.getAlert.bind(this);
}
componentDidMount() {
this.props.setClick(this.getAlert);
}
getAlert() {
alert('clicked');
}
render() {
return (
<h1 ref="hello">Hello</h1>
);
}
}
它的作用是在子级挂载时设置父级的
clickChild
方法。这样,当您单击父级中的按钮时,它将调用
clickChild
,而后者又调用子级的
getAlert
。
如果您的子级使用
connect()
包装,此方法也有效,因此您不需要
getWrappedInstance()
黑客攻击。
请注意,您不能在父级中使用
onClick={this.clickChild
,因为当呈现父级时,子级尚未挂载,因此
this.clickChild
尚未分配。使用
onClick={() => this.clickChild()
是可以的,因为当您单击按钮时,
this.clickChild
应该已经分配。
使用 useEffect 的替代方法:
父级:
const [refresh, doRefresh] = useState(0);
<Button onClick={() => doRefresh(prev => prev + 1)} />
<Children refresh={refresh} />
子级:
useEffect(() => {
performRefresh(); //children function of interest
}, [props.refresh]);