ChatGPT解决这个技术问题 Extra ChatGPT

从父调用子方法

我有两个组件:

父组件 子组件

我试图从 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>
    );
  }
}

有没有办法从 Parent 调用 Child 的方法?

注意:子组件和父组件位于两个不同的文件中。

您可以进行继承反转(在此处查找:medium.com/@franleplant/…)。这样您就可以访问要包装的组件的实例(因此您将能够访问它的功能)

r
rossipedia

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

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

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

现代 React with Hooks (v16.8+)

const { forwardRef, useRef, useImperativeHandle } = React; // 我们需要将组件包装在 `forwardRef` 中,以便获得 // 访问使用 `ref` 属性分配的 ref 对象。 // 这个 ref 作为第二个参数传递给函数组件。 const Child = forwardRef((props, ref) => { // 组件实例将被扩展 // 你从回调中返回的任何内容 // 作为第二个参数 useImperativeHandle(ref, () => ({ getAlert() { alert("getAlert from Child"); } })); return

Hi

; }); const Parent = () => { // 为了获得子组件实例的访问权, // 你需要将它分配给一个`ref`,所以我们调用`useRef()`来获取一个 const childRef = useRef( ); return (
); }; ReactDOM.render( , document.getElementById('root') );

useImperativeHandle() 的文档是 here

useImperativeHandle 自定义使用 ref 时暴露给父组件的实例值。

使用类组件的旧版 API (>= react@16.4)

常量 { 组件 } = 反应;类父扩展组件{构造函数(道具){超级(道具); this.child = React.createRef(); } onClick = () => { this.child.current.getAlert(); }; render() { return (

); } } 类子扩展组件 { getAlert() { alert('getAlert from Child'); } 渲染() { 返回

你好

; } } ReactDOM.render(, document.getElementById('root'));

回调参考 API

回调样式的 refs 是实现此目的的另一种方法,尽管在现代 React 中并不常见:

常量 { 组件 } = 反应;常量 { 渲染 } = ReactDOM; class Parent extends Component { render() { return (

{ this.child = instance; }} />
); } } 类子扩展组件 { getAlert() { alert('clicked'); } 渲染() { 返回(

你好

); } } 渲染(<父/>,document.getElementById('app'));


我累了,但最终出现此错误“_this2.refs.child.getAlert 不是函数”
那是因为 connect 返回包装原始实例的高阶组件。您需要首先在连接的组件上调用 getWrappedInstance() 以获取您的原始组件。然后你可以调用实例方法。
这不是一个很好的模式。更不用说字符串引用是不受欢迎的。最好将 props 传递给子组件,然后在父组件中单击按钮更改父组件的状态,并将状态项传递给子组件,这将触发子组件的 componentWillReceiveProps,并将其用作触发器。
不,这通常不是最好的模式,它更像是您需要时的逃生舱口,只应在紧急情况下使用。此外,这个答案是在字符串引用仍然存在时编写的,而且你说得对,它们现在不是“正确”的做事方式。
如果最佳实践是创建一个逻辑迷宫来做一些简单的事情,比如调用子组件的方法——那么我不同意最佳实践。
d
daaawx

您可以在此处使用另一种模式:

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() hack。

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


我得到 _this2.clickChild is not a function 为什么?
都没有工作。只有这个答案有效:github.com/kriasoft/react-starter-kit/issues/…
这是一个有趣的技术。它很干净,似乎没有违反任何规则。但我确实认为,如果您添加绑定,您的答案会更完整(并符合预期)。我非常喜欢这个答案,我已将其发布在 this related Github issue 上。
这应该是公认的答案
F
Freewalker

使用 useEffect 的替代方法:

家长:

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

孩子们:

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

附言。如果您只想重新渲染表单(例如,重置输入字段),那么您甚至不需要包含 useEffect,您只需将发送到组件中的道具更改
@tonymayoral 有没有办法我们可以在 Child 组件中使用 useState 并从 Parent 使用 doRefresh。就我而言,我不希望我的父母重新渲染。
此解决方案非常适合一次刷新或调用多个孩子的函数!
@MattFletcher 没有 useEffect,您可能会收到无限循环
这很好用。请注意,您还可以选中 (props.refresh !== 0) 以避免在初始循环中运行该函数。
S
Shamseer Ahammed

在这里,我将为您提供可能发生的四种可能的组合:

班级家长 |钩子钩父 |班级儿童挂钩家长 |钩子班家长 |班级儿童

1. 班级家长 |钩子

class Parent extends React.Component {
  constructor(props) {
    super(props)
    this.myRef = React.createRef()
  }

  render() {
    return (<View>
      <Child ref={this.myRef}/>
      <Button title={'call me'}
              onPress={() => this.myRef.current.childMethod()}/>
    </View>)
  }
}

const Child = React.forwardRef((props, ref) => {

  useImperativeHandle(ref, () => ({
    childMethod() {
      childMethod()
    }
  }))

  function childMethod() {
    console.log('call me')
  }

  return (<View><Text> I am a child</Text></View>)
})

2. 挂钩父 |班级儿童

function Parent(props) {

  const myRef = useRef()

  return (<View>
    <Child ref={myRef}/>
    <Button title={'call me'}
            onPress={() => myRef.current.childMethod()}/>
  </View>)
}

class Child extends React.Component {

  childMethod() {
    console.log('call me')
  }

  render() {
    return (<View><Text> I am a child</Text></View>)
  }
}

3. 钩父 |钩子

function Parent(props) {

  const myRef = useRef()

  return (<View>
    <Child ref={myRef}/>
    <Button title={'call me'}
            onPress={() => myRef.current.childMethod()}/>
  </View>)
}

const Child = React.forwardRef((props, ref) => {

  useImperativeHandle(ref, () => ({
    childMethod() {
      childMethod()
    }
  }))

  function childMethod() {
    console.log('call me')
  }

  return (<View><Text> I am a child</Text></View>)
})

4. 班级家长 |班级儿童

class Parent extends React.Component {
  constructor(props) {
    super(props)
    this.myRef = React.createRef()
  }

  render() {
    return (<View>
      <Child ref={this.myRef}/>
      <Button title={'call me'}
              onPress={() => this.myRef.current.childMethod()}/>
    </View>)
  }
}

class Child extends React.Component {

  childMethod() {
    console.log('call me')
  }

  render() {
    return (<View><Text> I am a child</Text></View>)
  }
}

这应该是公认的答案
对于 Hook Parent | Hook Child 为什么必须使用 forwardRef?为什么不能只通过 props.ref 访问 Child 内部的 ref 然后用 props.ref.childMethod=childMethod 分配?
第二个 ref 参数仅在您使用 React.forwardRef 调用定义组件时存在。常规函数或类组件不接收 ref 参数,并且 ref 在 props 中也不可用。
C
Community

https://facebook.github.io/react/tips/expose-component-functions.html 更多答案请参考这里Call methods on React children components

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

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


这是此答案的来源:discuss.reactjs.org/t/…。引用别人没有问题,但至少提供一些参考。
与 props 相比,这究竟是如何破坏封装的?
J
Johann

我对这里提出的任何解决方案都不满意。实际上有一个非常简单的解决方案,可以使用纯 Javascript 来完成,而不依赖于基本 props 对象以外的一些 React 功能 - 它为您提供了在任一方向(父 -> 子,子 -> 父)进行通信的好处。您需要将对象从父组件传递给子组件。这个对象就是我所说的“双向引用”或简称 biRef。基本上,该对象包含对父级想要公开的父级方法的引用。子组件将方法附加到父可以调用的对象。像这样的东西:

// Parent component.
function MyParentComponent(props) {

   function someParentFunction() {
      // The child component can call this function.
   }

   function onButtonClick() {
       // Call the function inside the child component.
       biRef.someChildFunction();
   }

   // Add all the functions here that the child can call.
   var biRef = {
      someParentFunction: someParentFunction
   }

   return <div>
       <MyChildComponent biRef={biRef} />
       <Button onClick={onButtonClick} />
   </div>;
}


// Child component
function MyChildComponent(props) {

   function someChildFunction() {
      // The parent component can call this function.
   }


   function onButtonClick() {
      // Call the parent function.
      props.biRef.someParentFunction();
   }

   // Add all the child functions to props.biRef that you want the parent
   // to be able to call.
   props.biRef.someChildFunction = someChildFunction;

   return <div>
       <Button onClick={onButtonClick} />
   </div>;
}

此解决方案的另一个优点是,您可以在父项和子项中添加更多功能,同时仅使用单个属性将它们从父项传递给子项。

对上面代码的改进是不将父函数和子函数直接添加到 biRef 对象,而是添加到子成员。父函数应添加到名为“parent”的成员中,而子函数应添加到名为“child”的成员中。

// Parent component.
function MyParentComponent(props) {

   function someParentFunction() {
      // The child component can call this function.
   }

   function onButtonClick() {
       // Call the function inside the child component.
       biRef.child.someChildFunction();
   }

   // Add all the functions here that the child can call.
   var biRef = {
      parent: {
          someParentFunction: someParentFunction
      }
   }

   return <div>
       <MyChildComponent biRef={biRef} />
       <Button onClick={onButtonClick} />
   </div>;
}


// Child component
function MyChildComponent(props) {

   function someChildFunction() {
      // The parent component can call this function.
   }


   function onButtonClick() {
      // Call the parent function.
      props.biRef.parent.someParentFunction();
   }

   // Add all the child functions to props.biRef that you want the parent
   // to be able to call.
   props.biRef {
       child: {
            someChildFunction: someChildFunction
       }
   }

   return <div>
       <Button onClick={onButtonClick} />
   </div>;
}

通过将父函数和子函数放置在 biRef 对象的不同成员中,您将清楚地分隔两者,并轻松查看哪些属于父函数或子函数。如果相同的函数出现在两个父函数中,它还有助于防止子组件意外覆盖父函数。

最后一件事是,如果您注意到,父组件使用 var 创建 biRef 对象,而子组件通过 props 对象访问它。在父级中不定义 biRef 对象并通过其自己的 props 参数从其父级访问它可能很诱人(在 UI 元素的层次结构中可能就是这种情况)。这是有风险的,因为孩子可能会认为它在父级上调用的函数属于父级,而实际上它可能属于祖父母。只要你意识到这一点,这没有什么错。除非您有理由支持超出父/子关系的某些层次结构,否则最好在父组件中创建 biRef。


这很好用,但它违背了反应的心态吗?
非常干燥。刚刚实现了这个,确认它有效
P
Peter Hollingsworth

我希望我没有从上面重复任何内容,但是传递一个在父级中设置函数的回调道具呢?这很有效,而且很容易。 (添加的代码在 //// 之间)

class Parent extends Component {
  ///// 
  getAlert = () => {} // initial value for getAlert

  setGetAlertMethod = (newMethod) => {
    this.getAlert = newMethod;
  }
  /////

  render() {
    return (
      <Child setGetAlertMethod={this.setGetAlertMethod}>
        <button onClick={this.getAlert}>Click</button>
      </Child>
      );
    }
  }



class Child extends Component {
  /////
  componentDidMount() {
    this.props.setGetAlertMethod(this.getAlert);
  }
  /////

  getAlert() => {
    alert('clicked');
  }

  render() {
    return (
      <h1 ref="hello">Hello</h1>
    );
  }
}

您在 Child 中的 getAlert 定义有错字。应该是`getAlert = () =>; { 警报(“点击”); } ` 其次,为什么 <button onClick={this.getAlert}>Click</button><Child> 内?重点是从 PARENT 调用子方法。
M
Muhammad Numan

您可以使用 ref 从父组件调用子组件的功能

功能组件解决方案

在功能组件中,您必须使用 useImperativeHandle 将 ref 放入如下所示的子项中

import React, { forwardRef, useRef, useImperativeHandle } from 'react';
export default function ParentFunction() {
    const childRef = useRef();
    return (
        <div className="container">
            <div>
                Parent Component
            </div>
            <button
                onClick={() => { childRef.current.showAlert() }}
            >
            Call Function
            </button>
            <Child ref={childRef}/>
        </div>
    )
}
const Child = forwardRef((props, ref) => {
    useImperativeHandle(
        ref,
        () => ({
            showAlert() {
                alert("Child Function Called")
            }
        }),
    )
    return (
       <div>Child Component</div>
    )
})

类组件解决方案

Child.js

import s from './Child.css';

class Child extends Component {
 getAlert() {
    alert('clicked');
 }
 render() {
  return (
    <h1>Hello</h1>
  );
 }
}

export default Child;

父.js

class Parent extends Component {
 render() {
  onClick() {
    this.refs.child.getAlert();
  }
  return (
    <div>
      <Child ref="child" />
      <button onClick={this.onClick}>Click</button>
    </div>
  );
 }
}

S
S.Yadav

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

我们将创建一个 Parent 元素,它将呈现一个 <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>
        );
    }
}  

现在,按照之前的理论设计,子组件将如下所示:

class Child extends React.Component {
    callChildMethod() {
        alert('Hello World');
        // to return some value
        // return this.state.someValue;
    }

    render() {
        return (
            <h1>Hello</h1>
        );
    }
}

这是source code-
希望对您有所帮助!


j
joeytwiddle

如果您这样做只是因为您希望 Child 向其父母提供可重用的 trait,那么您可能会考虑这样做 using render-props

该技术实际上将结构颠倒过来。 Child 现在包装了父级,因此我在下面将其重命名为 AlertTrait。我保留了名称 Parent 以保持连续性,尽管它现在不是真正的父母。

// Use it like this:

  <AlertTrait renderComponent={Parent}/>


class AlertTrait extends Component {
  // You will need to bind this function, if it uses 'this'
  doAlert() {
    alert('clicked');
  }
  render() {
    return this.props.renderComponent({ doAlert: this.doAlert });
  }
}

class Parent extends Component {
  render() {
    return (
      <button onClick={this.props.doAlert}>Click</button>
    );
  }
}

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

Parent 接收 doAlert 作为道具,并且可以在需要时调用它。

(为清楚起见,我在上面的示例中将 prop 称为 renderComponent。但在上面链接的 React 文档中,他们只是将其称为 render。)

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

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


这里有一个方便的创建可重用特征的模式列表:reactjs.org/blog/2016/07/13/…
如果你有 N 个秒表和一个按钮来重新启动它们怎么办。在这里渲染道具如何方便?
@vsync 我不确定这种方法是否可以帮助您完成任务。但 brickingup's answer 可能会有所帮助。请注意,它们设置了 this.clickChild = click,但您的多个秒表会传递多个函数,因此您需要存储所有这些:this.watchRestartFuncs[watchId] = restartWatch
B
Black Mamba

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

导入 { useEffect, useState } 从“反应”; export const ParentComponent = () => { const [trigger, setTrigger] = useState(false); return (

{ setTrigger(trigger => !trigger); }}>
); }; export const ChildComponent = (props) => { const triggerInvokedFromParent = () => { console.log('TriggerInvokedFromParent'); }; useEffect(() => { triggerInvokedFromParent(); }, [props.trigger]);返回 子组件; };


R
Rohit Parte

对于功能组件最简单的方法是

父组件

父.tsx

import React, { useEffect, useState, useRef } from "react";
import child from "../../child"

const parent: React.FunctionComponent = () => {
  const childRef: any = useRef();
}

const onDropDownChange: any = (event): void => {
    const target = event.target;
    childRef.current.onFilterChange(target.value);
};

return <child ref={childRef} />

export default parent;

子组件

孩子.tsx

import React, {   useState,   useEffect,   forwardRef,   useRef,   useImperativeHandle, } from "react";

const Child = forwardRef((props, ref) => {
 useImperativeHandle(ref, () => ({
    onFilterChange(id) {
      console.log("Value from parent", id)
    },
  }));
})

Child.displayName = "Child";

export default Child;

父母中的大括号不正确,请修复
H
Haseeb A

逻辑很简单。

Create a function in parent using child or use ref.

我更喜欢使用 child 的 parent 中的创建功能。有多种方法可以做到这一点。

使用功能组件时

在父级

function Parent(){
  const [functionToCall, createFunctionToCall] = useState(()=>()=>{})

  return (
   <Child createFunctionToCall={createFunctionToCall} />
  )
}

在儿童

function Child({createFunctionToCall}){
  useEffect(()=>{
    function theFunctionToCall(){
      // do something like setting something
      // don't forget to set dependancies properly.
    }
    createFunctionToCall(()=>theFunctionToCall)
  },[createFunctionToCall])
}


A
AmirHossein Rezaei

此模式类似于@brickingup answer。但在这个版本中,您可以设置任意数量的子操作。

import { useEffect } from "react";

export const Parent = () => {
  const childEvents = { click: () => {} };

  return (
    <div onClick={() => childEvents.click()}>
      <Child events={childEvents}></Child>
    </div>
  );
};

export const Child = (props) => {
  const click = () => {
    alert("click from child");
  };

  useEffect(() => {
    if (props.events) {
      props.events.click = click;
    }
  }, []);

  return <span>Child Component</span>;
};

F
Freewalker

我们对我们称为 useCounterKey 的自定义钩子感到满意。它只是设置了一个 counterKey,或者一个从零开始计数的键。它返回的函数重置密钥(即增量)。 (我相信这是重置组件的 the most idiomatic way in React - 只需敲击键即可。)

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

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

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

示例用法:

// 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]);

  // ...
}

(归功于 counterKey concept 的 Kent Dodds。)


L
Lukáš Novák

父组件

import Child from './Child'

export default function Parent(props) {
    const [childRefreshFunction, setChildRefreshFunction] = useState(null);

    return (
        <div>
            <button type="button" onClick={() => {
                childRefreshFunction();
            }}>Refresh child</button>
            <Child setRefreshFunction={(f) => {
                setChildRefreshFunction(f);
            }} />
        </div>
    )
}

子组件

export default function Child(props) {

    useEffect(() => {
        props.setRefreshFunction(() => refreshMe);
    }, []);

    function refreshMe() {
        fetch('http://example.com/data.json')....
    };

    return (
        <div>
            child
        </div>
    )
}

我没有意识到有人可以做到这一点。这是否比 useRef 更糟,或者可能不是“正确”的做法?我只是问,因为我真的很喜欢这个解决方案,我不明白为什么没有人向我建议。
这对我有用。您可能需要先检查孩子的 useEffect useEffect(() => { if( props.setRefreshFunction ){ props.setRefreshFunction(() => refreshMe); }, []); 否则,您将得到 setRefreshFunction undefined。
K
Kusal Kithmal

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

脚步-

在父类的状态中创建一个布尔变量。当你想调用一个函数时更新它。创建一个 prop 变量并分配布尔变量。从子组件中使用 props 访问该变量,并通过 if 条件执行您想要的方法。 class Child extends Component { Method=()=>{ --你的方法体-- } render() { return ( //检查变量是否更新 if(this.props.updateMethod){ this.Method( ); } ) } } class Parent extends Component { constructor(){ this.state={ callMethod:false } } render() { return ( //根据你的要求更新状态 this.setState({ callMethod:true }} <子 updateMethod={this.state.callMethod}> ); } }


您可能希望对此进行沙箱处理。看起来您最终将陷入无限循环,因为子方法将不断运行,因为父状态设置为 true。
@IsaacPak 是的,这就是我在那里留下评论的原因,说您必须根据您的要求更新状态。然后它不会作为无限循环运行。
W
Webwoman

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

我正在使用 useEffect 调用子组件的方法。我尝试过使用 Proxy and Setter_Getter,但到目前为止 useEffect 似乎是从父级调用子方法的更方便的方法。使用 Proxy and Setter_Getter 似乎需要先克服一些微妙之处,因为首先渲染的元素是通过 ref.current return => <div/> 的特殊性的 objectLike 的元素。关于 useEffect,您还可以利用这种方法来设置父级的状态,具体取决于您想对子级执行的操作。

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

在这里,我只为您提供我的 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>
       )
}

B
Bruno Pop

您可以使用您的子组件作为反应自定义挂钩非常轻松地应用该逻辑。

如何实施?

您的孩子返回一个函数。

您的孩子返回 JSON:{function, HTML, or other values} 作为示例。

In the example doesn't make sense to apply this logic but it is easy to see:

常量 {useState} = 反应; //Parent const Parent = () => { //自定义钩子 const child = useChild(); return (

{child.display} {child.btn}
); }; //子 const useChild = () => { const [clickCount, setClick] = React.useState(0); {/* 子按钮*/} const btn = ( ); return { btn: btn, //从父级调用的函数 alert: () => { alert("You clicked " + clickCount + " times"); },显示:

{clickCount}

}; }; const rootElement = document.getElementById("root"); ReactDOM.render(, rootElement);


我的子组件中现有的 useEffect 怎么样?
@匿名你是什么意思?
b
bvdb

我认为调用方法的最基本方式是在子组件上设置请求。然后一旦孩子处理了请求,它就会调用一个回调方法来重置请求。

重置机制对于能够在彼此之后多次发送相同的请求是必要的。

在父组件中

在父级的渲染方法中:

const { request } = this.state;
return (<Child request={request} onRequestHandled={()->resetRequest()}/>);

父母需要 2 种方法,在 2 个方向上与其孩子进行交流。

sendRequest() {
  const request = { param: "value" };
  this.setState({ request });
}

resetRequest() {
  const request = null;
  this.setState({ request });
}

在子组件中

孩子更新其内部状态,从道具复制请求。

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;
}

然后最后它处理请求,并将重置发送给父级:

componentDidMount() {
  const { request } = this.state;
  // todo handle request.

  const { onRequestHandled } = this.props;
  if (onRequestHandled != null) onRequestHandled();
}

C
Calsal

从父组件触发子函数的另一种方法是使用子组件中的 componentDidUpdate 函数。我将 prop triggerChildFunc 从 Parent 传递给 Child,最初是 null。单击按钮时,该值更改为函数,并且 Child 注意到 componentDidUpdate 中的更改并调用其自己的内部函数。

由于 prop triggerChildFunc 更改为函数,我们还获得了对 Parent 的回调。如果 Parent 不需要知道函数何时被调用,则值 triggerChildFunc 可以例如从 null 更改为 true

常量 { 组件 } = 反应;常量 { 渲染 } = ReactDOM;类父扩展组件 { state = { triggerFunc: null } render() { return (

); } } 类子扩展组件 { componentDidUpdate(prevProps) { if (this.props.triggerChildFunc !== prevProps.triggerChildFunc) { this.onParentTrigger(); } } onParentTrigger() { alert('父母触发了我'); // 如果它是一个函数,让我们从父级调用传递的变量 if (this.props.triggerChildFunc && {}.toString.call(this.props.triggerChildFunc) === '[object Function]') { this.props.triggerChildFunc (); } } 渲染() { 返回(

Hello

); } } 渲染(<父/>,document.getElementById('app'));

代码笔:https://codepen.io/calsal/pen/NWPxbJv?editors=1010


c
canbax

我尝试使用 createRefuseRef。不知何故,他们都返回 null

其次,this answer 建议传递一个 prop,它设置一个对我来说似乎最合理的 function。但是如果您的子组件在多个地方使用,您应该将额外的 prop 添加到其他地方。此外,如果您想在孙子中调用方法,则此方法可能过于冗长或过于冗长。

所以我以一种非常原始的方式制作了自己的函数库。

以下是 functionStore.js 文件

const fns = {};

export function setFn(componentName, fnName, fn) {
  if (fns[componentName]) {
    fns[componentName][fnName] = fn;
  } else {
    fns[componentName] = { fnName: fn };
  }
}

export function callFn(componentName, fnName) {
  fns[componentName][fnName]();
}

我只是设置了需要从任何组件调用的函数。

import { setFn } from "./functionStore";
export class AComponent extends React.Component {
  componentDidMount() {
    setFn("AComponent", "aFunc", this.aFunc);
  }
  aFunc = () => { console.log("aFunc is called!"); };
}

然后我只是从其他组件调用它

import { callFn } from "./functionStore";
export class BComponent extends React.Component {
  
  // just call the function
  bFunc = () => { 
    callFn("AComponent", "aFunc");
  };
}

一个缺点是要调用的函数应该是无参数的。但这也可能以某种方式解决。目前,我不需要传递参数。


B
Bhargav Rao

这是一个错误?要注意:我同意 rossipedia 使用 forwardRef、useRef、useImperativeHandle 的解决方案

网上有一些错误信息说 refs 只能从 React Class 组件创建,但如果你使用上面提到的 hooks,你确实可以使用 Function Components。请注意,只有在我将文件更改为在导出组件时不使用 withRouter() 之后,这些钩子才对我有用。即从

export default withRouter(TableConfig);

改为

export default TableConfig;

事后看来,这样的组件无论如何都不需要 withRouter(),但通常它不会伤害任何东西。我的用例是我创建了一个组件来创建一个表来处理配置值的查看和编辑,并且我希望能够告诉这个子组件在父窗体的重置按钮被点击时重置它的状态值。直到我从包含我的子组件 TableConfig 的文件中删除 withRouter() 之前,UseRef() 不会正确获取 ref 或 ref.current (保持为空)