เรียกวิธีเด็กจากผู้ปกครอง


474

ฉันมีสององค์ประกอบ

  1. องค์ประกอบหลัก
  2. องค์ประกอบลูก

ฉันพยายามเรียกวิธีการของเด็กจากผู้ปกครองฉันลองวิธีนี้ แต่ไม่สามารถรับผลได้

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 ดังนั้นฉันอยากรู้เกี่ยวกับกรณีที่ผู้ปกครองจะต้องเรียกวิธีเด็ก คุณช่วยอธิบายได้ไหม
Akshay Raut

ใครรู้วิธีการนี้จากฟังก์ชั่นลูกศร? stackoverflow.com/questions/60015693/…
Thomas Segato

@AkshayRaut IMO กรณีการใช้งานที่ดี: แบบฟอร์มวัตถุประสงค์ทั่วไปที่มีการรีเซ็ตและส่งฟังก์ชั่นซึ่งในภายหลังจะส่งคืนค่าแบบฟอร์ม
Julian K

คำตอบ:


702

ก่อนอื่นให้ฉันแสดงให้เห็นว่าโดยทั่วไปนี่ไม่ใช่วิธีที่จะไปเกี่ยวกับสิ่งต่าง ๆ ใน React land โดยปกติแล้วสิ่งที่คุณต้องการจะทำคือการทำงานผ่านลงให้กับเด็กในอุปกรณ์ประกอบฉากและผ่านการแจ้งเตือนจากเด็กที่อยู่ในเหตุการณ์ที่เกิดขึ้น (หรือดีกว่ายัง: dispatch)

แต่ถ้าคุณจะต้องเปิดเผยวิธีการที่จำเป็นในส่วนเด็กคุณสามารถใช้refs โปรดจำไว้ว่านี่คือฟักไข่และมักจะระบุว่ามีการออกแบบที่ดีกว่า

ก่อนหน้านี้การอ้างอิงได้รับการสนับสนุนสำหรับส่วนประกอบแบบคลาสเท่านั้น ด้วยการถือกำเนิดของReact Hooksนั่นไม่ใช่กรณีอีกต่อไป

การใช้ hooks และฟังก์ชั่นส่วนประกอบ ( >= react@16.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()อยู่ที่นี่ :

useImperativeHandlerefปรับแต่งค่ากรณีที่มีการสัมผัสกับชิ้นส่วนผู้ปกครองเมื่อใช้

ใช้ส่วนประกอบระดับ ( >= 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>

Legacy API ( <= react@16.3)

สำหรับวัตถุประสงค์ในอดีตนี่คือรูปแบบการโทรกลับที่คุณจะใช้กับรุ่น React ก่อน 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>


23
ฉันเหนื่อย แต่จบลงด้วยข้อผิดพลาด "_this2.refs.child.getAlert นี้ไม่ใช่ฟังก์ชั่น"
N8FURY

22
นั่นเป็นเพราะconnectส่งคืนส่วนประกอบคำสั่งซื้อที่สูงขึ้นซึ่งจะทำให้อินสแตนซ์ดั้งเดิมของคุณครอบคลุม คุณจะต้องโทรหาอุปกรณ์getWrappedInstance()ที่เชื่อมต่อก่อนเพื่อรับส่วนประกอบดั้งเดิมของคุณ จากนั้นคุณสามารถเรียกใช้เมธอดอินสแตนซ์ได้
rossipedia

16
นี่ไม่ใช่รูปแบบที่ดีจริงๆ ไม่พูดถึงการอ้างอิงสตริงจะขมวดคิ้วเมื่อ เป็นการดีกว่าที่จะส่งอุปกรณ์ประกอบฉากไปยังองค์ประกอบของเด็กและจากนั้นคลิกปุ่มในผู้ปกครองเปลี่ยนสถานะของผู้ปกครองและผ่านรายการของรัฐเข้าสู่เด็กซึ่งจะเรียกเด็กcomponentWillReceivePropsและใช้เป็นทริกเกอร์
ffxsam

7
ไม่มีก็ไม่ได้มักจะมีรูปแบบที่ดีที่สุดก็มากขึ้นของหนีออกจากไข่เมื่อคุณต้องการมันและควรจะใช้เฉพาะในกรณีฉุกเฉิน นอกจากนี้คำตอบนี้ถูกเขียนขึ้นเมื่อการอ้างอิงสตริงยังคงอยู่และคุณคิดถูกว่าไม่ใช่วิธีที่ถูกต้องในการทำสิ่งต่าง ๆ ในปัจจุบัน
rossipedia

35
หากวิธีปฏิบัติที่ดีที่สุดคือการสร้างเขาวงกตของตรรกะเพื่อทำสิ่งที่ง่ายเหมือนการเรียกวิธีการขององค์ประกอบเด็ก - ฉันก็ไม่เห็นด้วยกับวิธีปฏิบัติที่ดีที่สุด
aaaaaa

149

คุณสามารถใช้รูปแบบอื่นได้ที่นี่:

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วิธีการของผู้ปกครองเมื่อติดตั้งลูกคืออะไร ด้วยวิธีนี้เมื่อคุณคลิกปุ่มในการปกครองก็จะเรียกซึ่งเรียกร้องของเด็กclickChildgetAlert

วิธีนี้ใช้ได้ผลถ้าลูกของคุณห่อด้วยconnect()ดังนั้นคุณจึงไม่ต้องการgetWrappedInstance()แฮ็ค

หมายเหตุคุณไม่สามารถใช้onClick={this.clickChild}ในพาเรนต์ได้เนื่องจากเมื่อเรนเดอร์เรนเดอร์ลูกไม่ถูกเมาท์ดังนั้นthis.clickChildยังไม่ได้กำหนด การใช้onClick={() => this.clickChild()}ไม่เป็นไรเพราะเมื่อคุณคลิกปุ่มthis.clickChildควรกำหนดให้


5
ฉันได้รับ_this2.clickChild is not a functionทำไม?
tatsu

1
ไม่เป็นไรมันใช้งานได้กับฉัน: github.com/kriasoft/react-starter-kit/issues/…
tatsu

5
ไม่ทำงาน คำตอบนี้ใช้ได้เฉพาะ: github.com/kriasoft/react-starter-kit/issues/
......

2
นี่เป็นเทคนิคที่น่าสนใจ มันค่อนข้างสะอาดและดูเหมือนจะไม่ผิดกฎใด ๆ แต่ฉันคิดว่าคำตอบของคุณจะสมบูรณ์มากขึ้น (และตอบสนองความคาดหวัง) หากคุณเพิ่มการผูกไว้ ผมชอบคำตอบมากผมได้โพสต์ไว้ในประเด็นที่เกี่ยวข้อง Github นี้
joeytwiddle

7
นี่ควรเป็นคำตอบที่ถูกต้อง
Jesus Gomez

27

https://facebook.github.io/react/tips/expose-component-functions.html สำหรับคำตอบเพิ่มเติมอ้างอิงที่นี่วิธีการโทรในการตอบโต้องค์ประกอบเด็ก ๆ

เมื่อมองเข้าไปในการอ้างอิงขององค์ประกอบ "เหตุผล" คุณกำลังทำลาย encapsulation และทำให้ไม่สามารถ refactor ส่วนประกอบนั้นโดยไม่ตรวจสอบสถานที่ทั้งหมดที่ใช้อย่างระมัดระวัง ด้วยเหตุนี้เราจึงแนะนำอย่างยิ่งให้ปฏิบัติการอ้างอิงในฐานะส่วนตัวกับองค์ประกอบเช่นเดียวกับสถานะ

โดยทั่วไปข้อมูลควรถูกส่งผ่านต้นไม้ผ่านอุปกรณ์ประกอบฉาก มีข้อยกเว้นบางประการสำหรับเรื่องนี้ (เช่นการเรียก. โฟกัส () หรือเรียกภาพเคลื่อนไหวเพียงครั้งเดียวที่ไม่ได้ "เปลี่ยน" รัฐ) แต่เมื่อใดก็ตามที่คุณเปิดเผยวิธีการที่เรียกว่า "ชุด" อุปกรณ์ประกอบฉากมักจะ ทางเลือกที่ดีกว่า พยายามทำให้ส่วนประกอบอินพุตด้านในกังวลกับขนาดและรูปลักษณ์ของมันเพื่อไม่ให้บรรพบุรุษของมันทำ


5
นี่คือที่มาของคำตอบนี้: discuss.reactjs.org/t/... ไม่มีปัญหากับการอ้างถึงผู้อื่น แต่อย่างน้อยก็ใส่ในการอ้างอิงบางอย่าง
Jodo

1
การหยุดพักนี้ห่อหุ้มอุปกรณ์ประกอบฉากมากแค่ไหน?
Timmmm

15

วิธีการอื่นที่มี useEffect:

ต้นทาง:

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

เด็ก:

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

2
นี่ควรได้รับการโหวตมากขึ้น
อาลีอัลอามีน

ps หากความต้องการของคุณเพียงแสดงฟอร์มอีกครั้ง (ตัวอย่างเช่นเพื่อรีเซ็ตฟิลด์อินพุต) จากนั้นคุณไม่จำเป็นต้องรวม useEffect คุณสามารถทำให้เสาถูกส่งไปยังการเปลี่ยนแปลงองค์ประกอบ
Matt Fletcher

8

เราสามารถใช้การอ้างอิงด้วยวิธีอื่นตาม -

เราจะสร้างองค์ประกอบผู้ปกครองมันจะแสดง<Child/>องค์ประกอบ ดังที่คุณเห็นส่วนประกอบที่จะแสดงผลคุณจะต้องเพิ่มแอตทริบิวต์การอ้างอิงและตั้งชื่อให้
จากนั้นtriggerChildAlertฟังก์ชั่นที่อยู่ในระดับผู้ปกครองจะเข้าถึงคุณสมบัติ 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>
        );
    }
}

นี่คือซอร์สโค้ด -
หวังว่าจะช่วยคุณได้!


1
การอ้างอิงสตริงจะถูกคัดค้าน reactjs.org/docs/refs-and-the-dom.html#legacy-api-string-refs
Cory McAboy

4

หากคุณทำสิ่งนี้เพียงเพราะคุณต้องการให้เด็กมอบอุปนิสัยที่สามารถนำกลับมาใช้ใหม่ให้กับผู้ปกครองได้คุณอาจพิจารณาทำเช่นนั้น โดยใช้อุปกรณ์แสดงผลแทน

เทคนิคนั้นเปลี่ยนโครงสร้างกลับหัว Childตอนนี้ wraps แม่ดังนั้นผมจึงได้เปลี่ยนชื่อเป็นไป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>
    );
  }
}

ในกรณีนี้ AlertTrait ให้คุณสมบัติอย่างน้อยหนึ่งอย่างที่มันผ่านลงมาเป็นอุปกรณ์ประกอบฉากกับสิ่งที่มันได้รับใน renderComponentเสา

ผู้ปกครองได้รับ doAlertเป็นเสาและสามารถเรียกได้เมื่อจำเป็น

(เพื่อความชัดเจนฉันเรียกว่าเสา renderComponentในตัวอย่างด้านบน แต่ในเอกสาร React ที่ลิงก์ด้านบนพวกเขาเรียกมันว่าrender)

คอมโพเนนต์ Trait สามารถสร้างการแสดงผลสิ่งรอบ ๆ พาเรนต์ในฟังก์ชันการเรนเดอร์ แต่มันจะไม่เรนเดอร์อะไรภายในพาเรนต์ ที่จริงมันสามารถทำให้สิ่งต่าง ๆ ภายในผู้ปกครองถ้ามันผ่านเสาอื่น (เช่นrenderChild ) ไปยังผู้ปกครองซึ่งผู้ปกครองสามารถใช้ในระหว่างวิธีการแสดงผล

สิ่งนี้ค่อนข้างแตกต่างจากที่ OP ขอ แต่บางคนอาจมาที่นี่ (เหมือนที่เราทำ) เพราะพวกเขาต้องการสร้างลักษณะที่สามารถนำกลับมาใช้ใหม่ได้และคิดว่าองค์ประกอบเด็กเป็นวิธีที่ดีในการทำเช่นนั้น


มีรายการรูปแบบที่สะดวกสำหรับการสร้างลักษณะที่สามารถนำกลับมาใช้ใหม่ได้ที่นี่: reactjs.org/blog/2016/07/13/ …
joeytwiddle

จะทำอย่างไรถ้าคุณมีนาฬิกาจับเวลาไม่ระบุและกดปุ่มเดียวเพื่อรีสตาร์ททั้งหมด อุปกรณ์แสดงภาพมีประโยชน์อย่างไรที่นี่?
vsync

@vsync ฉันไม่แน่ใจว่าวิธีนี้จะช่วยงานของคุณได้อย่างไร แต่คำตอบของ brickingupอาจช่วยได้ โปรดทราบว่าพวกเขาตั้งค่าไว้this.clickChild = clickแต่นาฬิกาจับเวลาหลายตัวของคุณจะผ่านฟังก์ชั่นหลายอย่างดังนั้นคุณจะต้องเก็บทุกอย่างไว้ด้วย:this.watchRestartFuncs[watchId] = restartWatch
joeytwiddle

1

คุณสามารถทำได้อย่างง่ายดายด้วยวิธีนี้

Steps-

  1. สร้างตัวแปรบูลีนในสถานะในคลาสพาเรนต์ อัปเดตสิ่งนี้เมื่อคุณต้องการเรียกใช้ฟังก์ชัน
  2. สร้างตัวแปร prop และกำหนดตัวแปรบูลีน
  3. จากองค์ประกอบลูกเข้าถึงตัวแปรนั้นโดยใช้อุปกรณ์ประกอบฉากและดำเนินการตามวิธีที่คุณต้องการโดยมีเงื่อนไขว่า

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

คุณอาจต้องการที่จะแซนด์บ็อกซ์นี้ ดูเหมือนว่าคุณกำลังจะสิ้นสุดด้วยลูปไม่สิ้นสุดเนื่องจากเมธอด child จะรันอย่างต่อเนื่องเนื่องจากสถานะพาเรนต์ถูกตั้งค่าเป็นจริง
Isaac Pak

@IsaacPak ใช่นั่นเป็นเหตุผลที่ฉันทิ้งความคิดเห็นไว้ที่นั่นโดยบอกว่าคุณต้องอัปเดตสถานะตามความต้องการของคุณ จากนั้นมันจะไม่ทำงานเป็นวงวนไม่สิ้นสุด
Kusal Kithmal

1

ฉันใช้useEffectเบ็ดเพื่อเอาชนะอาการปวดหัวจากการทำทั้งหมดดังนั้นตอนนี้ฉันผ่านตัวแปรลงไปยังเด็กเช่นนี้:

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

1

ฉันไม่พอใจกับวิธีแก้ไขปัญหาใด ๆ ที่นำเสนอที่นี่ จริงๆแล้วมันมีวิธีง่ายๆที่สามารถทำได้โดยใช้ Javascript ล้วนๆโดยไม่ต้องอาศัยฟังก์ชั่น React อื่นนอกเหนือจากวัตถุประกอบฉากพื้นฐาน - และมันให้ประโยชน์ในการสื่อสารในทิศทางใดทิศทางหนึ่ง (parent -> child, child -> parent) คุณต้องผ่านวัตถุจากองค์ประกอบหลักไปยังองค์ประกอบลูก วัตถุนี้เป็นสิ่งที่ฉันเรียกว่า "การอ้างอิงสองทิศทาง" หรือ 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>;
}

ข้อดีอีกประการของวิธีนี้คือคุณสามารถเพิ่มฟังก์ชั่นได้มากขึ้นใน parent และ child ในขณะที่ส่งผ่านจาก parent ไปยัง child โดยใช้คุณสมบัติเดียวเท่านั้น

การปรับปรุงรหัสข้างต้นคือการไม่เพิ่มฟังก์ชั่นหลักและลูกโดยตรงไปยังวัตถุ 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 คุณจะมีการแยกที่ชัดเจนระหว่างทั้งสองและดูได้อย่างง่ายดายว่าสิ่งใดเป็นของพ่อแม่หรือลูก นอกจากนี้ยังช่วยป้องกันไม่ให้องค์ประกอบลูกจากการเขียนทับฟังก์ชั่นผู้ปกครองโดยไม่ตั้งใจถ้าฟังก์ชั่นเดียวกันปรากฏขึ้นในทั้งสอง

สิ่งสุดท้ายคือถ้าคุณสังเกตองค์ประกอบหลักสร้างวัตถุ biRef กับ var ในขณะที่องค์ประกอบลูกเข้าถึงวัตถุผ่านวัตถุประกอบฉาก มันอาจเป็นการล่อลวงที่จะไม่กำหนดวัตถุ biRef ในพาเรนต์และเข้าถึงมันจากพาเรนต์ผ่านพารามิเตอร์ props ของตัวเอง (ซึ่งอาจเป็นกรณีในลำดับชั้นขององค์ประกอบ UI) สิ่งนี้มีความเสี่ยงเพราะเด็กอาจคิดว่าเป็นหน้าที่ของผู้ปกครองที่เป็นของพ่อแม่เมื่อจริง ๆ แล้วมันอาจเป็นของปู่ย่าตายาย ไม่มีอะไรผิดปกติกับเรื่องนี้ตราบใดที่คุณตระหนักถึงมัน ยกเว้นว่าคุณมีเหตุผลในการสนับสนุนลำดับชั้นบางอย่างที่นอกเหนือจากความสัมพันธ์ของผู้ปกครอง / เด็กควรสร้าง biRef ในองค์ประกอบหลักของคุณ


0

คุณสามารถทำการ Inheritance Inversion (ค้นหาได้ที่นี่: https://medium.com/@franleplant/react-higher-order-components-in-depth-cf9032ee6c3e ) วิธีนี้ทำให้คุณสามารถเข้าถึงอินสแตนซ์ของส่วนประกอบที่คุณต้องการห่อ (ดังนั้นคุณจะสามารถเข้าถึงฟังก์ชั่นได้)


0

ฉันคิดว่าวิธีพื้นฐานที่สุดในการโทรเมธอดคือการตั้งค่าคำขอบนคอมโพเนนต์ลูก จากนั้นทันทีที่เด็กจัดการคำขอจะเรียกวิธีการโทรกลับเพื่อรีเซ็ตคำขอ

กลไกการรีเซ็ตจำเป็นต้องสามารถส่งคำขอเดียวกันหลายครั้งหลังจากกัน

ในองค์ประกอบหลัก

ในวิธีการเรนเดอร์ของพาเรนต์:

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

0

อีกวิธีในการทริกเกอร์ฟังก์ชั่นเด็กจากผู้ปกครองคือการใช้ประโยชน์จากcomponentDidUpdateฟังก์ชั่นในองค์ประกอบเด็ก ฉันผ่านเสาจากแม่เพื่อเด็กซึ่งเป็นครั้งแรกtriggerChildFunc nullค่าจะเปลี่ยนเป็นฟังก์ชั่นเมื่อคลิกปุ่มและลูกแจ้งให้ทราบว่ามีการเปลี่ยนแปลงcomponentDidUpdateและเรียกใช้ฟังก์ชันภายในของตัวเอง

ตั้งแต่ prop triggerChildFuncเปลี่ยนเป็นฟังก์ชั่นเรายังได้รับการเรียกกลับไปยังผู้ปกครอง หากผู้ปกครองไม่จำเป็นต้องรู้เมื่อฟังก์ชั่นที่เรียกว่าค่า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


0

นี่คือตัวอย่างของฉัน: https://stackblitz.com/edit/react-dgz1ee?file=styles.css

ฉันใช้useEffectเพื่อเรียกวิธีการขององค์ประกอบเด็ก ฉันได้ลองด้วยแล้วProxy and Setter_Getterแต่useEffectดูเหมือนว่าไกลเป็นวิธีที่สะดวกกว่าในการโทรหาวิธีเด็กจากผู้ปกครอง เมื่อต้องการใช้Proxy and Setter_Getterดูเหมือนว่ามีความละเอียดอ่อนที่จะเอาชนะก่อนเพราะองค์ประกอบที่แสดงผลครั้งแรกเป็นองค์ประกอบของ objectLike ผ่านความref.current return => <div/>จำเพาะของ เกี่ยวกับ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>
       )
}

0

useCounterKeyเรามีความสุขอยู่กับการเรียกเราเบ็ดที่กำหนดเอง มันเพิ่งตั้งค่า counterKey หรือกุญแจที่นับจากศูนย์ ฟังก์ชั่นที่ส่งกลับจะรีเซ็ตคีย์ (เช่นการเพิ่มขึ้น) (ฉันเชื่อว่านี่เป็นวิธีที่ใช้สำนวนที่สุดใน Reactเพื่อรีเซ็ตส่วนประกอบ - เพียงแค่กดปุ่ม)

อย่างไรก็ตาม hook นี้ยังทำงานในสถานการณ์ใด ๆ ที่คุณต้องการส่งข้อความขาจรไปยังไคลเอนต์เพื่อทำบางสิ่งบางอย่าง เช่นเราใช้เพื่อมุ่งเน้นการควบคุมในเด็กในเหตุการณ์หลักบางอย่าง - เพียงปรับโฟกัสอัตโนมัติทุกครั้งที่มีการอัปเดตคีย์ (หากจำเป็นต้องมีอุปกรณ์ประกอบฉากมากขึ้นพวกเขาสามารถตั้งค่าก่อนที่จะรีเซ็ตคีย์เพื่อให้สามารถใช้ได้เมื่อมีเหตุการณ์เกิดขึ้น)

วิธีนี้มีเส้นโค้งการเรียนรู้เล็กน้อย / ไม่ตรงไปตรงมาเป็นตัวจัดการเหตุการณ์ทั่วไป แต่ดูเหมือนว่าเป็นวิธีที่ใช้สำนวนที่สุดในการจัดการสิ่งนี้ในการตอบสนองที่เราได้พบ เปิดรับฟังความคิดเห็นเกี่ยวกับวิธีการนี้ แต่ใช้งานได้ดี!

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


-1

นี่คือข้อบกพร่องหรือไม่? ระวัง: ฉันเห็นด้วยกับวิธีแก้ปัญหาของ rossipedia โดยใช้ forwardRef, useRef, useImperativeHandle

มีข้อมูลที่ไม่ถูกต้องทางออนไลน์ที่ระบุว่า refs สามารถสร้างได้จากส่วนประกอบของ React Class เท่านั้น แต่คุณสามารถใช้ Function Components ได้หากคุณใช้ hooks ดังกล่าวข้างต้น หมายเหตุตะขอใช้งานได้เฉพาะกับฉันหลังจากฉันเปลี่ยนไฟล์เป็นไม่ใช้ withRouter () เมื่อส่งออกส่วนประกอบ นั่นคือการเปลี่ยนแปลงจาก

export default withRouter(TableConfig);

เพื่อแทนที่จะเป็น

export default TableConfig;

ใน hindsight withRouter () ไม่จำเป็นสำหรับส่วนประกอบดังกล่าว แต่โดยทั่วไปแล้วมันไม่เจ็บอะไรเลยในกรณีที่ใช้ของฉันคือฉันสร้างส่วนประกอบเพื่อสร้างตารางเพื่อจัดการกับการดูและแก้ไขค่าการตั้งค่า และฉันต้องการที่จะบอกส่วนประกอบของเด็กนี้เพื่อรีเซ็ตเป็นค่าสถานะเมื่อใดก็ตามที่กดปุ่มรีเซ็ตของรูปแบบผู้ปกครอง UseRef () จะไม่ได้รับการอ้างอิงหรือ ref.current (เก็บไว้เป็นโมฆะ) จนกว่าฉันจะลบ withRouter () จากไฟล์ที่มีองค์ประกอบลูกของฉัน TableConfig

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.