可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I have two components.
- Parent component
- Child component
I was trying to call child\'s method from Parent, I tried this way but couldn\'t get a result
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>
);
}
}
Is there a way to call child\'s method from the parent ?
Note: Child and Parent components are in two different files
回答1:
You can use refs
Previously, refs were only supported for Class-based components. With
the advent of React Hooks, that\'s no longer the case
Using Hooks and Function Components
import React, { forwardRef, useRef, useImperativeMethods } from \'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
useImperativeMethods(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>
);
};
Functioning example
Documentation for useImperativeMethods()
is here:
useImperativeMethods
customizes the instance value that is exposed to parent components when using ref
.
Using Class Components
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\'));
Functioning Example
Legacy API (React < 16.3)
For historical purposes, here\'s the callback-based style you\'d use with React versions before 16.3:
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>
回答2:
You can use another pattern here:
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>
);
}
}
What it does is to set the parent\'s clickChild method when child is mounted. In this way when you click the button in parent it will call clickChild which calls child\'s getAlert.
This also works if your child is wrapped with connect() so you don\'t need the getWrappedInstance() hack.
Note you can\'t use onClick={this.clickChild} in parent because when parent is rendered child is not mounted so this.clickChild is not assigned yet. Using onClick={() => this.clickChild()} is fine because when you click the button this.clickChild should already be assigned.
回答3:
https://facebook.github.io/react/tips/expose-component-functions.html
for more answers ref here Call methods on React children components
By looking into the refs of the \"reason\" component, you\'re breaking encapsulation and making it impossible to refactor that component without carefully examining all the places it\'s used. Because of this, we strongly recommend treating refs as private to a component, much like state.
In general, data should be passed down the tree via props. There are a few exceptions to this (such as calling .focus() or triggering a one-time animation that doesn\'t really \"change\" the state) but any time you\'re exposing a method called \"set\", props are usually a better choice. Try to make it so that the inner input component worries about its size and appearance so that none of its ancestors do.
回答4:
If you are doing this simply because you want the Child to provide a re-usable trait to its parents, then you might consider doing that using render-props instead.
That technique actually turns the structure upside down. The Child
now wraps the parent, so I have renamed it to AlertTrait
below. I kept the name Parent
for continuity, although it is not really a parent now.
// Use it like this:
<AlertTrait renderComponent={Parent}/>
class AlertTrait extends Component {
// You may need to bind this function, if it is stateful
doAlert() {
alert(\'clicked\');
}
render() {
return this.props.renderComponent(this.doAlert);
}
}
class Parent extends Component {
render() {
return (
<button onClick={this.props.doAlert}>Click</button>
);
}
}
In this case, the AlertTrait provides one or more traits which it passes down as props to whatever component it was given in its renderComponent
prop.
The Parent receives doAlert
as a prop, and can call it when needed.
(For clarity, I called the prop renderComponent
in the above example. But in the React docs linked above, they just call it render
.)
The Trait component can render stuff surrounding the Parent, in its render function, but it does not render anything inside the parent. Actually it could render things inside the Parent, if it passed another prop (e.g. renderChild
) to the parent, which the parent could then use during its render method.
This is somewhat different from what the OP asked for, but some people might end up here (like we did) because they wanted to create a reusable trait, and thought that a child component was a good way to do that.
回答5:
We can use refs in another way as-
We are going to create a Parent element, it will render a <Child/>
component. As you can see, the component that will be rendered, you need to add the ref attribute and provide a name for it.
Then, the triggerChildAlert
function, located in the parent class will access the refs property of the this context (when the triggerChildAlert
function is triggered will access the child reference and it will has all the functions of the child element).
class Parent extends React.Component {
triggerChildAlert(){
this.refs.child.callChildMethod();
// to get child parent returned value-
// this.value = this.refs.child.callChildMethod();
// alert(\'Returned value- \'+this.value);
}
render() {
return (
<div>
{/* Note that you need to give a value to the ref parameter, in this case child*/}
<Child ref=\"child\" />
<button onClick={this.triggerChildAlert}>Click</button>
</div>
);
}
}
Now, the child component, as theoretically designed previously, will look like:
class Child extends React.Component {
callChildMethod() {
alert(\'Hello World\');
// to return some value
// return this.state.someValue;
}
render() {
return (
<h1>Hello</h1>
);
}
}
Here is the source code-
Hope will help you !
回答6:
You can make Inheritance Inversion (look it up here: https://medium.com/@franleplant/react-higher-order-components-in-depth-cf9032ee6c3e). That way you have access to instance of the component that you would be wrapping (thus you\'ll be able to access it\'s functions)
回答7:
I think that the most basic way to call methods is by setting a request on the child component. Then as soon as the child handles the request, it calls a callback method to reset the request.
The reset mechanism is necessary to be able to send the same request multiple times after each other.
In parent component
In the render method of the parent:
const { request } = this.state;
return (<Child request={request} onRequestHandled={()->resetRequest()}/>);
The parent needs 2 methods, to communicate with its child in 2 directions.
sendRequest() {
const request = { param: \"value\" };
this.setState({ request });
}
resetRequest() {
const request = null;
this.setState({ request });
}
In child component
The child updates its internal state, copying the request from the props.
constructor(props) {
super(props);
const { request } = props;
this.state = { request };
}
static getDerivedStateFromProps(props, state) {
const { request } = props;
if (request !== state.request ) return { request };
return null;
}
Then finally it handles the request, and sends the reset to the parent:
componentDidMount() {
const { request } = this.state;
// todo handle request.
const { onRequestHandled } = this.props;
if (onRequestHandled != null) onRequestHandled();
}