簡體   English   中英

從父級調用子方法

[英]Call child method from parent

我有兩個組成部分:

  1. 父組件
  2. 子組件

我試圖從 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 的方法?

注意:子組件和父組件位於兩個不同的文件中。

首先,讓我表示,這通常不是React 領域的處理方式。 通常你想要做的是將功能傳遞給 props 中的孩子,並在事件中傳遞來自孩子的通知(或者更好的是: dispatch )。

但是如果你必須在子組件上公開一個命令式方法,你可以使用refs 請記住,這是一個逃生艙口,通常表明有更好的設計可用。

以前,僅基於類的組件支持 refs。 隨着React 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自定義在使用ref時暴露給父組件的實例值。

使用類組件的舊 API ( >= react@16.4 )

 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>

回調引用 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方法。 這樣,當您單擊父級中的按鈕時,它會調用clickChild ,后者調用子級的getAlert

如果您的孩子使用connect()包裹,這也有效,因此您不需要getWrappedInstance() hack。

請注意,您不能在父級中使用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]);

https://facebook.github.io/react/tips/expose-component-functions.html有關更多答案,請參考此處調用 React 子組件上的方法

通過查看“原因”組件的引用,您破壞了封裝,並且如果不仔細檢查它使用的所有地方就無法重構該組件。 因此,我們強烈建議將 refs 視為組件私有的,就像狀態一樣。

一般來說,數據應該通過 props 沿着樹向下傳遞。 對此有一些例外(例如調用 .focus() 或觸發一次並沒有真正“改變”狀態的動畫)但是任何時候你暴露一個名為“set”的方法,道具通常是更好的選擇。 盡量讓內部輸入組件擔心它的大小和外觀,這樣它的祖先就不會擔心了。

我對這里提供的任何解決方案都不滿意。 實際上有一個非常簡單的解決方案,可以使用純 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。

我希望我沒有重復上面的任何內容,但是傳遞一個在父級中設置函數的回調道具呢? 這很有效,而且很容易。 (添加的代碼位於 //// 之間)

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

我們可以以另一種方式使用 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>
        );
    }
}

這是源代碼-
希望能幫到你!

如果您這樣做僅僅是因為您希望 Child 為其父母提供可重用的 trait,那么您可以考慮使用 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,並可以在需要時調用它。

(為了清楚起見,我在上面的示例中將 prop renderComponent稱為。但在上面鏈接的 React 文檔中,他們只是將其稱為render 。)

Trait 組件可以在其渲染函數中渲染 Parent 周圍的東西,但它不會在父級內部渲染任何東西。 實際上,它可以在 Parent 內部渲染東西,如果它將另一個 prop(例如renderChild )傳遞給父級,然后父級可以在其渲染方法中使用。

這與 OP 要求的有些不同,但有些人可能會在這里(就像我們所做的那樣)因為他們想要創建一個可重用的特性,並認為子組件是實現這一目標的好方法。

在這里,我將給您四種可能發生的組合:

  1. 班級家長 | 鈎子
  2. 鈎父 | 班級兒童
  3. 鈎父 | 鈎子
  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>)
  }
}

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

鈎父 | 班級兒童

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

鈎父 | 鈎子

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

班級家長 | 班級兒童

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

您可以使用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>
    )
})

類組件解決方案

兒童.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>
  );
 }
}

我們對稱為useCounterKey的自定義鈎子感到useCounterKey 它只是設置一個 counterKey,或者一個從零開始計數的鍵。 它返回的函數重置鍵(即增量)。 (我相信這是React中重置組件的最慣用的方式- 只需按一下鍵即可。)

然而,這個鈎子也適用於您想要向客戶端發送一次性消息以執行某些操作的任何情況。 例如,我們使用它來將子控件中的控件聚焦在某個父事件上——它只是在鍵更新時自動聚焦。 (如果需要更多道具,可以在重置鍵之前設置它們,以便在事件發生時可用。)

這種方法有一點學習曲線 b/c 它不像典型的事件處理程序那么簡單,但它似乎是我們發現的在 React 中處理這個問題的最慣用的方法(因為鍵已經以這種方式起作用)。 對此方法的反饋非常開放,但它運行良好!

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

  // ...
}

感謝Kent Dodds 的counterKey 概念。)

邏輯很簡單。

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])
}

您可以通過這種方式輕松實現這一目標

腳步-

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

這是我的演示: 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>
       )
}

您可以進行繼承反轉(在此處查找: https : //medium.com/@franleplant/react-higher-order-components-in-depth-cf9032ee6c3e )。 這樣您就可以訪問要包裝的組件的實例(因此您將能夠訪問它的功能)

我認為調用方法的最基本方法是在子組件上設置請求。 然后一旦孩子處理了請求,它就會調用回調方法來重置請求。

重置機制是必要的,以便能夠多次發送相同的請求。

在父組件中

在父級的渲染方法中:

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

在子組件中

child 更新其內部狀態,從 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;
}

最后它處理請求,並將重置發送給父級:

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

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

從父級觸發子函數的另一種方法是使用子組件中的componentDidUpdate函數。 我將一個 prop triggerChildFunc從 Parent 傳遞給 Child,它最初是null 單擊按鈕時,該值會更改為函數,並且 Child 會注意到componentDidUpdate中的更改並調用其自己的內部函數。

由於 prop triggerChildFunc更改為一個函數,因此我們也獲得了對 Parent 的回調。 如果 Parent 不需要知道函數何時被調用,則值triggerChildFunc可以例如從null更改為true

 const { Component } = React; const { render } = ReactDOM; class Parent extends Component { state = { triggerFunc: null } render() { return ( <div> <Child triggerChildFunc={this.state.triggerFunc} /> <button onClick={() => { this.setState({ triggerFunc: () => alert('Callback in parent')}) }}>Click </button> </div> ); } } class Child extends Component { componentDidUpdate(prevProps) { if (this.props.triggerChildFunc !== prevProps.triggerChildFunc) { this.onParentTrigger(); } } onParentTrigger() { alert('parent triggered me'); // Let's call the passed variable from parent if it's a function if (this.props.triggerChildFunc && {}.toString.call(this.props.triggerChildFunc) === '[object Function]') { this.props.triggerChildFunc(); } } render() { return ( <h1>Hello</h1> ); } } render( <Parent />, document.getElementById('app') );
 <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script> <div id='app'></div>

CodePen: https ://codepen.io/calsal/pen/NWPxbJv ? editors = 1010

我正在使用useEffect鈎子來克服做這一切的頭痛,所以現在我將一個變量傳遞給孩子,如下所示:

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

這是一個錯誤? 注意:我同意 rossipedia 使用 forwardRef、useRef、useImperativeHandle 的解決方案

網上有一些錯誤信息說 refs 只能從 React Class 組件創建,但是如果您使用上面提到的鈎子,您確實可以使用函數組件。 請注意,在導出組件時將文件更改為不使用 withRouter() 后,鈎子僅對我有用。 即從

export default withRouter(TableConfig);

而是成為

export default TableConfig;

事后看來,這種組件無論如何都不需要 withRouter(),但通常它不會傷害任何東西。我的用例是我創建了一個組件來創建一個表來處理配置值的查看和編輯,並且我希望能夠告訴這個子組件在父窗體的重置按鈕被點擊時重置它的狀態值。 在我從包含我的子組件 TableConfig 的文件中刪除 withRouter() 之前,UseRef() 不會正確獲取 ref 或 ref.current(保持為空)

您可以使用您的子組件作為 react自定義 hook非常輕松地應用該邏輯。

如何實施?

  • 你的孩子返回一個函數。

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

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

 const {useState} = React; //Parent const Parent = () => { //custome hook const child = useChild(); return ( <div> {child.display} <button onClick={child.alert}> Parent call child </button> {child.btn} </div> ); }; //Child const useChild = () => { const [clickCount, setClick] = React.useState(0); {/* child button*/} const btn = ( <button onClick={() => { setClick(clickCount + 1); }} > Click me </button> ); return { btn: btn, //function called from parent alert: () => { alert("You clicked " + clickCount + " times"); }, display: <h1>{clickCount}</h1> }; }; const rootElement = document.getElementById("root"); ReactDOM.render(<Parent />, rootElement);
 <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script> <div id="root"></div>

對於功能組件最簡單的方法是

父組件

父.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;

父組件

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

這種模式類似於@brickingup answer 但在此版本中,您可以設置任意數量的子操作。

import { useEffect } from "react";

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

  return (
    <div onClick={() => childEvents.click()}>
      <Child></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>;
};

我嘗試使用createRefuseRef 不知何故,它們都返回null

其次,這個答案建議傳遞一個設置一個對我來說似乎最合理的functionprop 但是如果您的子組件在多個地方使用,您應該將該額外的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");
  };
}

一個缺點是要調用的函數應該是無參數的。 但這也可能以某種方式修復。 目前,我不需要傳遞參數。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM