เรียกใช้วิธีการตอบสนององค์ประกอบจากภายนอก


95

ฉันต้องการเรียกวิธีที่เปิดเผยโดยองค์ประกอบการตอบสนองจากอินสแตนซ์ขององค์ประกอบการตอบสนอง

ตัวอย่างเช่นในjsfiddleนี้ ฉันต้องการเรียกalertMessageวิธีการจากการHelloElementอ้างอิง

มีวิธีที่จะบรรลุโดยไม่ต้องเขียนห่อเพิ่มเติมหรือไม่?

แก้ไข (คัดลอกโค้ดจาก JSFiddle)

<div id="container"></div>
<button onclick="onButtonClick()">Click me!</button>
var onButtonClick = function () {

    //call alertMessage method from the reference of a React Element! Something like HelloElement.alertMessage()
    console.log("clicked!");
}

var Hello = React.createClass({displayName: 'Hello',

    alertMessage: function() {
        alert(this.props.name);                             
    },

    render: function() {
        return React.createElement("div", null, "Hello ", this.props.name);
    }
});

var HelloElement = React.createElement(Hello, {name: "World"});

React.render(
    HelloElement,
    document.getElementById('container')
);

3
ไม่เหมาะ แต่ JSFiddle เป็นเรื่องธรรมดาพอที่จะไม่รับประกันการลงคะแนน
Jeff Fairley

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

คำตอบ:


56

มีสองวิธีในการเข้าถึงฟังก์ชันภายใน หนึ่งระดับอินสแตนซ์อย่างที่คุณต้องการอีกระดับคงที่

ตัวอย่าง

React.renderคุณจำเป็นต้องเรียกใช้ฟังก์ชันในการกลับมาจาก ดูด้านล่าง

คงที่

ลองดูที่ReactJS สถิตยศาสตร์ แต่โปรดทราบว่าการทำงานคงสามารถเข้าถึงข้อมูลที่ไม่ได้เช่นระดับดังนั้นจะthisundefined

var onButtonClick = function () {
    //call alertMessage method from the reference of a React Element! 
    HelloRendered.alertMessage();
    //call static alertMessage method from the reference of a React Class! 
    Hello.alertMessage();
    console.log("clicked!");
}

var Hello = React.createClass({
    displayName: 'Hello',
    statics: {
        alertMessage: function () {
            alert('static message');
        }
    },
    alertMessage: function () {
        alert(this.props.name);
    },

    render: function () {
        return React.createElement("div", null, "Hello ", this.props.name);
    }
});

var HelloElement = React.createElement(Hello, {
    name: "World"
});

var HelloRendered = React.render(HelloElement, document.getElementById('container'));

แล้วทำHelloRendered.alertMessage().


13
โปรดทราบว่าการใช้ค่าส่งคืนของการแสดงผลถือว่าเลิกใช้แล้วและคาดว่าจะถูกลบออกในเวอร์ชันต่อ ๆ ไปเพื่อเปิดใช้งานการปรับปรุงประสิทธิภาพ วิธีที่ได้รับการสนับสนุนในการอ้างอิงถึงออบเจ็กต์อินสแตนซ์คอมโพเนนต์ของคุณคือการเพิ่มคุณสมบัติrefซึ่งเป็นฟังก์ชันที่เรียกใช้โดยมีอินสแตนซ์เป็นพารามิเตอร์ นอกจากนี้ยังช่วยให้คุณสามารถเข้าถึงวัตถุที่ไม่ได้อยู่ในระดับบนเช่นถ้าคุณกำลังแสดงผล<MuiThemeProvider><Hello ref={setHelloRef} /></MuiThemeProvider>ให้คุณได้รับการอ้างอิงที่ถูกต้องส่งผ่านไปยังคุณฟังก์ชั่นมากกว่าหนึ่งไปยังsetHelloRef MuiThemeProvider
Periata Breatta

Uncaught TypeError: _react2.default.render ไม่ใช่ฟังก์ชัน
Partha Paul

46

คุณสามารถทำเช่น

import React from 'react';

class Header extends React.Component{

    constructor(){
        super();
        window.helloComponent = this;
    }

    alertMessage(){
       console.log("Called from outside");
    }

    render(){

      return (
      <AppBar style={{background:'#000'}}>
        Hello
      </AppBar>
      )
    }
}

export default Header;

ตอนนี้จากภายนอกส่วนประกอบนี้คุณสามารถเรียกสิ่งนี้ได้ด้านล่าง

window.helloComponent.alertMessage();

2
ในความเป็นจริงง่ายและใช้งานได้! อย่างที่ควรจะเป็น ฉันประทับใจมากกับความเรียบง่าย ไม่ได้คิดเกี่ยวกับ อาจเป็นไปได้ว่าวิธีนี้จะไม่ได้ผลดีนักหากคุณควรมีส่วนประกอบมากขึ้นเรื่อย ๆ ขอบคุณ!
Leonardo Maffei

6
การเพิ่มตัวแปรส่วนกลางไม่ใช่ทางออกที่ดี เพิ่มเติมเกี่ยวกับสาเหตุที่ตัวแปรทั่วโลกไม่ดีที่นี่: wiki.c2.com/?GlobalVariablesAreBad
Salvatore Zappalà

4
ขอบคุณสำหรับการโหวต !! ใช่ตัวแปรส่วนกลางไม่ดี แต่เป็นวิธีแก้ปัญหาของคุณ
Kushal Jain

1
นี่เป็นวิธีแก้ปัญหาที่ใช้งานง่ายและง่ายที่ฉันต้องการขอบคุณ!
Florent Destremau

2
มันใช้งานได้ แต่จะไม่ทำงานหากคุณมีส่วนประกอบเดียวกันหลายอย่างในหน้าเดียวกัน
gaurav

26

ฉันได้ทำสิ่งนี้แล้ว:

class Cow extends React.Component {

    constructor (props) {
        super(props);
        this.state = {text: 'hello'};
    }

    componentDidMount () {
        if (this.props.onMounted) {
            this.props.onMounted({
                say: text => this.say(text)
            });
        }
    }

    render () {
        return (
            <pre>
                 ___________________
                < {this.state.text} >
                 -------------------
                        \   ^__^
                         \  (oo)\_______
                            (__)\       )\/\
                                ||----w |
                                ||     ||
            </pre>
        );
    }

    say (text) {
        this.setState({text: text});
    }

}

แล้วที่อื่น:

class Pasture extends React.Component {

    render () {
        return (
            <div>
                <Cow onMounted={callbacks => this.cowMounted(callbacks)} />
                <button onClick={() => this.changeCow()} />
            </div>
        );
    }

    cowMounted (callbacks) {
        this.cowCallbacks = callbacks;
    }

    changeCow () {
        this.cowCallbacks.say('moo');
    }

}

ฉันไม่ได้ทดสอบโค้ดที่แน่นอนนี้ แต่นี่เป็นไปตามสิ่งที่ฉันทำในโครงการของฉันและมันก็ใช้งานได้ดี :) แน่นอนว่านี่เป็นตัวอย่างที่ไม่ดีคุณควรใช้อุปกรณ์ประกอบฉากสำหรับสิ่งนี้ แต่ในกรณีของฉันส่วนประกอบย่อยทำการเรียก API ซึ่งฉันต้องการเก็บไว้ในส่วนประกอบนั้น ในกรณีนี้เป็นทางออกที่ดี


5
ฉันคิดว่าคุณหมายถึงthis.cowCallbacks.say('moo')
สตีเวน

BTW มันเป็นไปได้ที่จะผ่านthisไปยังโทรกลับ (ซึ่งจะเป็นตัวอย่างของวัว) callbacksแทน
WebBrother

@WebBrother ใช่ แต่นั่นจะยิ่ง
แฮ็ค

6

ด้วยrenderวิธีนี้อาจทำให้ค่าที่ส่งคืนกลับมาได้แนวทางที่แนะนำคือการแนบการอ้างอิงการเรียกกลับไปยังองค์ประกอบราก แบบนี้:

ReactDOM.render( <Hello name="World" ref={(element) => {window.helloComponent = element}}/>, document.getElementById('container'));

ซึ่งเราสามารถเข้าถึงได้โดยใช้ window.helloComponent และวิธีการใด ๆ สามารถเข้าถึงได้ด้วย window.helloComponent.METHOD

นี่คือตัวอย่างทั้งหมด:

var onButtonClick = function() {
  window.helloComponent.alertMessage();
}

class Hello extends React.Component {
  alertMessage() {
    alert(this.props.name);
  }

  render() {
    return React.createElement("div", null, "Hello ", this.props.name);
  }
};

ReactDOM.render( <Hello name="World" ref={(element) => {window.helloComponent = element}}/>, document.getElementById('container'));
<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="container"></div>
<button onclick="onButtonClick()">Click me!</button>


ดีกว่าที่จะวางการอ้างอิงในสถานะท้องถิ่นแทนที่จะเป็นวัตถุหน้าต่าง ... ref={component => {this.setState({ helloComponent: component; })}} จากนั้นในตัวจัดการการคลิก ... this.state.helloComponent.alertMessage();
Bill Dagg

นอกเหนือจากความคิดเห็นก่อนหน้าของฉัน ... หรือดีกว่านั้นเพียงแค่ใส่เมธอด alertMessage ของคอมโพเนนต์ให้อยู่ในสถานะ
Bill Dagg

เมื่อฉันลองสิ่งนี้ฉันได้รับWarning: Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?และมันไม่ได้ผูกส่วนประกอบกับหน้าต่าง
Guerrilla

4
class AppProvider extends Component {
  constructor() {
    super();

    window.alertMessage = this.alertMessage.bind(this);
  }

  alertMessage() {
    console.log('Hello World');
 }
}

คุณสามารถเรียกวิธีนี้จากหน้าต่างโดยใช้window.alertMessage().


วิธีนี้ใช้ได้ผล แต่การเพิ่มตัวแปรส่วนกลางไม่ใช่วิธีแก้ปัญหาที่ดีด้วยเหตุผลหลายประการ คุณสามารถค้นหาข้อมูลเพิ่มเติมเกี่ยวกับสาเหตุที่ตัวแปรส่วนกลางไม่ดีได้ที่นี่: wiki.c2.com/?GlobalVariablesAreBad
Salvatore Zappalà

3

หากคุณอยู่ใน ES6 เพียงแค่ใช้คำหลัก "คงที่" ในวิธีการของคุณจากตัวอย่างของคุณจะเป็นดังต่อไปนี้: static alertMessage: function() { ...
},

หวังว่าจะสามารถช่วยทุกคนได้ :)


คุณไม่สามารถเข้าถึงอุปกรณ์ประกอบฉากหรือสถานะในฟังก์ชันคงที่
Serdar Değirmenci

ตกลงใช่ แต่คำถามเกี่ยวกับการเข้าถึงฟังก์ชัน alertMessage () เพื่อให้คุณสามารถใช้ HelloElement.alertMessage ()
darmis

โดยทั่วไปการเรียกใช้ฟังก์ชันโดยไม่ใช้อุปกรณ์ประกอบฉากและสถานะจะไม่มีผล แต่ในขณะที่คุณพูดถูกเกี่ยวกับการเข้าถึงฟังก์ชันฉันกำลังลบการโหวตของฉัน
Serdar Değirmenci

3

ด้วย React hook - useRef



const MyComponent = ({myRef}) => {
  const handleClick = () => alert('hello world')
  myRef.current.handleClick = handleClick
  return (<button onClick={handleClick}>Original Button</button>)
}

MyComponent.defaultProps = {
  myRef: {current: {}}
}

const MyParentComponent = () => {
  const myRef = React.useRef({})
  return (
    <>
      <MyComponent 
        myRef={myRef}
      />
      <button onClick={myRef.current.handleClick}>
        Additional Button
      </button>
    </>
  )
}

โชคดี...


2

คุณสามารถเพิ่มonClickตัวจัดการให้กับ div ด้วยฟังก์ชัน ( onClickเป็นการใช้งานของ React เองonClick) และคุณสามารถเข้าถึงคุณสมบัติภายใน{ }วงเล็บปีกกาและข้อความแจ้งเตือนของคุณจะปรากฏขึ้น

ในกรณีที่คุณต้องการกำหนดวิธีการแบบคงที่ที่สามารถเรียกใช้ในคลาสคอมโพเนนต์ - คุณควรใช้สถิต แม้ว่า:

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

โค้ดตัวอย่างบางส่วน:

    const Hello = React.createClass({

        /*
            The statics object allows you to define static methods that can be called on the component class. For example:
        */
        statics: {
            customMethod: function(foo) {
              return foo === 'bar';
            }
        },


        alertMessage: function() {
            alert(this.props.name);                             
        },

        render: function () {
            return (
                <div onClick={this.alertMessage}>
                Hello {this.props.name}
                </div>
            );
        }
    });

    React.render(<Hello name={'aworld'} />, document.body);

หวังว่านี่จะช่วยคุณได้บ้างเพราะฉันไม่รู้ว่าฉันเข้าใจคำถามของคุณถูกต้องหรือไม่โปรดแก้ไขฉันถ้าฉันตีความผิด :)


2

ดูเหมือนว่าสถิตยศาสตร์จะถูกเลิกใช้และวิธีอื่น ๆ ในการเปิดเผยฟังก์ชันบางอย่างที่renderดูเหมือนซับซ้อน ในขณะเดียวกันคำตอบ Stack Overflow นี้เกี่ยวกับการดีบัก Reactในขณะที่ดูเหมือนว่าแฮ็ค y ทำงานให้ฉัน


2

วิธีที่ 1 using ChildRef :

public childRef: any = React.createRef<Hello>();

public onButtonClick= () => {
    console.log(this.childRef.current); // this will have your child reference
}

<Hello ref = { this.childRef }/>
<button onclick="onButtonClick()">Click me!</button>

วิธีที่ 2: using window register

public onButtonClick= () => {
    console.log(window.yourRef); // this will have your child reference
}

<Hello ref = { (ref) => {window.yourRef = ref} }/>`
<button onclick="onButtonClick()">Click me!</button>

วิธีที่ 1 เป็นวิธีที่ง่ายและสะอาดมากในการเข้าถึงวิธีส่วนประกอบย่อย ขอบคุณ!
gabdara

1

ฉันใช้วิธีผู้ช่วยนี้เพื่อแสดงผลคอมโพเนนต์และส่งคืนอินสแตนซ์คอมโพเนนต์ สามารถเรียกวิธีการในอินสแตนซ์นั้นได้

static async renderComponentAt(componentClass, props, parentElementId){
         let componentId = props.id;
        if(!componentId){
            throw Error('Component has no id property. Please include id:"...xyz..." to component properties.');
        }

        let parentElement = document.getElementById(parentElementId);

        return await new Promise((resolve, reject) => {
            props.ref = (component)=>{
                resolve(component);
            };
            let element = React.createElement(componentClass, props, null);
            ReactDOM.render(element, parentElement);
        });
    }
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.