Javascript 从父级调用子方法

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/37949981/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 20:54:20  来源:igfitidea点击:

Call child method from parent

javascriptreactjs

提问by N8FURY

I have two components.

我有两个组件。

  1. Parent component
  2. Child component
  1. 父组件
  2. 子组件

I was trying to call child's method from Parent, I tried this way but couldn't get a result

我试图从 Parent 调用 child 的方法,我尝试过这种方式但无法得到结果

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

注意:子组件和父组件在两个不同的文件中

回答by rossipedia

First off, let me express that this is generally notthe way to go about things in React land. Usually what you want to do is pass down functionality to children in props, and pass up notifications from children in events (or better yet: dispatch).

首先,让我表示,这通常不是React 领域的处理方式。通常你想要做的是在 props 中将功能传递给孩子,并在事件中传递来自孩子的通知(或者更好:)dispatch

But if you mustexpose an imperative method on a child component, you can use refs. Remember this is an escape hatch and usually indicates a better design is available.

但是如果你必须在子组件上公开一个命令式方法,你可以使用refs。请记住,这是一个逃生舱口,通常表明有更好的设计可用。

Previously, refs were only supported for Class-based components. With the advent of React Hooks, that's no longer the case

以前,仅基于类的组件支持 refs。随着React Hooks的出现,情况不再如此

Using Hooks and Function Components (>= [email protected])

使用钩子和函数组件 ( >= [email protected])

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>

Documentation for useImperativeHandle()is here:

对于文档useImperativeHandle()在这里

useImperativeHandlecustomizes the instance value that is exposed to parent components when using ref.

useImperativeHandle自定义在使用时向父组件公开的实例值ref

Using Class Components (>= [email protected])

使用类组件 ( >= [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>

Legacy API (<= [email protected])

旧 API ( <= [email protected])

For historical purposes, here's the callback-based style you'd use with React versions before 16.3:

出于历史目的,以下是您在 16.3 之前的 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>

回答by brickingup

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 clickChildmethod when child is mounted. In this way when you click the button in parent it will call clickChildwhich calls child's getAlert.

它所做的是在clickChild安装子项时设置父项的方法。这样,当您单击 parent 中的按钮时,它将调用clickChild调用 child 的getAlert.

This also works if your child is wrapped with connect()so you don't need the getWrappedInstance()hack.

如果您的孩子被包裹,这也有效,connect()因此您不需要getWrappedInstance()黑客。

Note you can't use onClick={this.clickChild}in parent because when parent is rendered child is not mounted so this.clickChildis not assigned yet. Using onClick={() => this.clickChild()}is fine because when you click the button this.clickChildshould already be assigned.

请注意,您不能onClick={this.clickChild}在父级中使用,因为当父级呈现时,子级尚未安装,因此this.clickChild尚未分配。使用onClick={() => this.clickChild()}很好,因为当你点击按钮时this.clickChild应该已经被分配了。

回答by Mike Tronic

https://facebook.github.io/react/tips/expose-component-functions.htmlfor more answers ref here Call methods on React children components

https://facebook.github.io/react/tips/expose-component-functions.html有关更多答案,请参考此处调用 React 子组件上的方法

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.

通过查看“原因”组件的引用,您破坏了封装,并且如果不仔细检查它使用的所有地方就无法重构该组件。因此,我们强烈建议将 refs 视为组件私有的,就像状态一样。

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.

一般来说,数据应该通过 props 沿着树向下传递。对此有一些例外(例如调用 .focus() 或触发一次并没有真正“改变”状态的动画)但是任何时候你暴露一个名为“set”的方法,道具通常是更好的选择。尽量让内部输入组件担心它的大小和外观,这样它的祖先就不会担心了。

回答by tonymayoral

Alternative method with useEffect:

使用 useEffect 的替代方法:

Parent:

家长:

const [refresh, doRefresh] = useState(0);
<Button onClick={()=>doRefresh(refresh+1)} />
<Children refresh={refresh} />

Children:

孩子们:

useEffect(() => {
    refresh(); //children function of interest
  }, [props.refresh]);

回答by S.Yadav

We can use refs in another way as-

我们可以以另一种方式使用 refs -

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 refattribute and provide a name for it.
Then, the triggerChildAlertfunction, located in the parent class will access the refs property of the this context (when the triggerChildAlertfunction is triggered will access the child reference and it will has all the functions of the child element).

我们将创建一个父元素,它将呈现一个<Child/>组件。如您所见,将要呈现的组件,您需要添加ref属性并为其提供名称。
然后,triggerChildAlert位于父类中的函数将访问 this 上下文的 refs 属性(当triggerChildAlert函数被触发时将访问子引用,它将拥有子元素的所有功能)。

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 !

这是源代码-
希望对您有所帮助!

回答by joeytwiddle

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-propsinstead.

如果您这样做仅仅是因为您希望 Child 为其父母提供可重用的 trait,那么您可以考虑使用 render-props来做到这一点。

That technique actually turns the structure upside down. The Childnow wraps the parent, so I have renamed it to AlertTraitbelow. I kept the name Parentfor continuity, although it is not really a parent now.

该技术实际上将结构颠倒了。在Child现在包装了父母,所以我改名到AlertTrait下面。我保留了Parent连续性的名字,尽管它现在不是真正的父母。

// 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 renderComponentprop.

在这种情况下, AlertTrait 提供一个或多个特征,它作为道具传递给它在其renderComponent道具中给出的任何组件。

The Parent receives doAlertas a prop, and can call it when needed.

ParentdoAlert作为 prop接收,可以在需要时调用它。

(For clarity, I called the prop renderComponentin the above example. But in the React docs linked above, they just call it render.)

(为了清楚起见,我renderComponent在上面的示例中调用了 prop 。但是在上面链接的 React 文档中,他们只是调用了它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.

Trait 组件可以在其渲染函数中渲染 Parent 周围的东西,但它不会在父级内部渲染任何东西。实际上,它可以在 Parent 内部渲染东西,如果它将另一个 prop(例如renderChild)传递给父级,然后父级可以在其渲染方法中使用。

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.

这与 OP 要求的有些不同,但有些人可能会在这里(就像我们所做的那样)因为他们想要创建一个可重用的特性,并认为子组件是实现这一目标的好方法。

回答by Kusal Kithmal

You can achieve this easily in this way

您可以通过这种方式轻松实现这一目标

Steps-

脚步-

  1. Create a boolean variable in the state in the parent class. Update this when you want to call a function.
  2. Create a prop variable and assign the boolean variable.
  3. From the child component access that variable using props and execute the method you want by having an if condition.

    class Child extends Component {
       Method=()=>{
       --Your method body--
       }
       render() {
         return (
        //check whether the variable has been updated or not
          if(this.props.updateMethod){
            this.Method();
          }
         )
       }
    }
    
    class Parent extends Component {
    
    constructor(){
      this.state={
       callMethod:false
      }
    
    }
    render() {
       return (
    
         //update state according to your requirement
         this.setState({
            callMethod:true
         }}
         <Child updateMethod={this.state.callMethod}></Child>
        );
       }
    }
    
  1. 在父类的状态中创建一个布尔变量。当你想调用一个函数时更新它。
  2. 创建一个 prop 变量并分配布尔变量。
  3. 从子组件使用 props 访问该变量并通过 if 条件执行您想要的方法。

    class Child extends Component {
       Method=()=>{
       --Your method body--
       }
       render() {
         return (
        //check whether the variable has been updated or not
          if(this.props.updateMethod){
            this.Method();
          }
         )
       }
    }
    
    class Parent extends Component {
    
    constructor(){
      this.state={
       callMethod:false
      }
    
    }
    render() {
       return (
    
         //update state according to your requirement
         this.setState({
            callMethod:true
         }}
         <Child updateMethod={this.state.callMethod}></Child>
        );
       }
    }
    

回答by Black Mamba

I'm using useEffecthook to overcome the headache of doing all this so now I pass a variable down to child like this:

我正在使用useEffect钩子来克服做这一切的头痛,所以现在我将一个变量传递给孩子,如下所示:

<ParentComponent>
 <ChildComponent arbitrary={value} />
</ParentComponent>
useEffect(() => callTheFunctionToBeCalled(value) , [value]);

回答by Webwoman

Here my demo: https://stackblitz.com/edit/react-dgz1ee?file=styles.css

这是我的演示:https: //stackblitz.com/edit/react-dgz1ee?file=styles.css

I am using useEffectto call the children component's methods. I have tried with Proxy and Setter_Getterbut sor far useEffectseems to be the more convenient way to call a child method from parent. To use Proxy and Setter_Getterit seems there is some subtlety to overcome first, because the element firstly rendered is an objectLike's element through the ref.current return => <div/>'s specificity. Concerning useEffect, you can also leverage on this approach to set the parent's state depending on what you want to do with the children.

useEffect用来调用子组件的方法。我已经尝试过,Proxy and Setter_Getter但到目前为止useEffect似乎是从父级调用子方法的更方便的方法。使用Proxy and Setter_Getter它似乎首先要克服一些微妙之处,因为首先呈现的元素是通过 的特殊性的 objectLike 元素ref.current return => <div/>。关于useEffect,您还可以利用这种方法根据您想对孩子做什么来设置父母的状态。

In the demo's link I have provided, you will find my full ReactJS' code with my draftwork inside's so you can appreciate the workflow of my solution.

在我提供的演示链接中,您将找到我完整的 ReactJS 代码以及我的草稿,以便您了解我的解决方案的工作流程。

Here I am providing you my ReactJS' snippet with the relevant code only. :

在这里,我仅向您提供包含相关代码的 ReactJS 代码段。:

import React, {
  Component,
  createRef,
  forwardRef,
  useState,
  useEffect
} from "react"; 

{...}

// Child component
// I am defining here a forwardRef's element to get the Child's methods from the parent
// through the ref's element.
let Child = forwardRef((props, ref) => {
  // I am fetching the parent's method here
  // that allows me to connect the parent and the child's components
  let { validateChildren } = props;
  // I am initializing the state of the children
  // good if we can even leverage on the functional children's state
  let initialState = {
    one: "hello world",
    two: () => {
      console.log("I am accessing child method from parent :].");
      return "child method achieve";
    }
  };
  // useState initialization
  const [componentState, setComponentState] = useState(initialState);
  // useEffect will allow me to communicate with the parent
  // through a lifecycle data flow
  useEffect(() => {
    ref.current = { componentState };
    validateChildren(ref.current.componentState.two);
  });

{...}

});

{...}

// Parent component
class App extends Component {
  // initialize the ref inside the constructor element
  constructor(props) {
    super(props);
    this.childRef = createRef();
  }

  // I am implementing a parent's method
  // in child useEffect's method
  validateChildren = childrenMethod => {
    // access children method from parent
    childrenMethod();
    // or signaling children is ready
    console.log("children active");
  };

{...}
render(){
       return (
          {
            // I am referencing the children
            // also I am implementing the parent logic connector's function
            // in the child, here => this.validateChildren's function
          }
          <Child ref={this.childRef} validateChildren={this.validateChildren} />
        </div>
       )
}

回答by Freewalker

We're happy with a custom hook we call useCounterKey. It just sets up a counterKey, or a key that counts up from zero. The function it returns resets the key (i.e. increment). (I believe this is the most idiomatic way in Reactto reset a component - just bump the key.)

我们对称为 的自定义钩子感到满意useCounterKey。它只是设置一个 counterKey,或者一个从零开始计数的键。它返回的函数重置键(即增量)。(我相信这是React中重置组件的最惯用的方式- 只需按一下键即可。)

However this hook also works in any situation where you want to send a one-time message to the client to do something. E.g. we use it to focus a control in the child on a certain parent event - it just autofocuses anytime the key is updated. (If more props are needed they could be set prior to resetting the key so they're available when the event happens.)

然而,这个钩子也适用于您想要向客户端发送一次性消息以执行某些操作的任何情况。例如,我们使用它来将子控件中的控件聚焦在某个父事件上——它只是在键更新时自动聚焦。(如果需要更多道具,可以在重置键之前设置它们,以便在事件发生时可用。)

This method has a bit of a learning curve b/c it's not as straightforward as a typical event handler, but it seems the most idiomatic way to handle this in React that we've found (since keys already function this way). Def open to feedback on this method but it is working well!

这种方法有一点学习曲线 b/c 它不像典型的事件处理程序那么简单,但它似乎是我们发现的在 React 中处理这个问题的最惯用的方法(因为键已经以这种方式起作用)。对此方法的反馈非常开放,但它运行良好!

// Main helper hook:
export function useCounterKey() {
  const [key, setKey] = useState(0);
  return [key, () => setKey(prev => prev + 1)] as const;
}

Sample usages:

示例用法:

// Sample 1 - normal React, just reset a control by changing Key on demand
function Sample1() {
  const [inputLineCounterKey, resetInputLine] = useCounterKey();

  return <>
    <InputLine key={inputLineCounterKey} />
    <button onClick={() => resetInputLine()} />
  <>;
}

// Second sample - anytime the counterKey is incremented, child calls focus() on the input
function Sample2() {
  const [amountFocusCounterKey, focusAmountInput] = useCounterKey();

  // ... call focusAmountInput in some hook or event handler as needed

  return <WorkoutAmountInput focusCounterKey={amountFocusCounterKey} />
}

function WorkoutAmountInput(props) {
  useEffect(() => {
    if (counterKey > 0) {
      // Don't focus initially
      focusAmount();
    }
  }, [counterKey]);

  // ...
}

(Credit to Kent Dodds for the counterKey concept.)

感谢Kent Dodds 的counterKey 概念。)