Wywołaj metodę komponentu React z zewnątrz


95

Chcę wywołać metodę ujawnioną przez składnik React z wystąpienia elementu React.

Na przykład w tym pliku jsfiddle . Chcę wywołać alertMessagemetodę z HelloElementreferencji.

Czy jest sposób na osiągnięcie tego bez konieczności pisania dodatkowych opakowań?

Edytuj (kod skopiowany z 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
Nie jest to idealne rozwiązanie, ale JSFiddle jest na tyle powszechne, że nie daje podstaw do głosowania przeciw.
Jeff Fairley,

Zastanawiam się, jaki może być twój przypadek użycia, który uzasadniałby coś takiego. To nie jest dobry sposób na projektowanie imo aplikacji. Jeśli potrzebujesz czegoś ponownie, utwórz osobny wspólny pomocnik w trzecim pliku i użyj go jako przycisku, a także komponentu reagowania.
aromatyzowany herbatą

Odpowiedzi:


56

Istnieją dwa sposoby uzyskania dostępu do funkcji wewnętrznej. Jeden, poziom instancji, tak jak chcesz, inny, statyczny poziom.

Instancja

Musisz wywołać funkcję po powrocie z React.render. Zobacz poniżej.

Statyczny

Spójrz na ReactJS Statics . Należy jednak pamiętać, że funkcja statyczna nie może uzyskać dostępu do danych na poziomie instancji, więc tak thisbędzie undefined.

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

Więc zrób HelloRendered.alertMessage().


13
Należy pamiętać, że użycie wartości zwracanej renderowania jest uważane za przestarzałe i oczekuje się, że zostanie usunięte w przyszłych wersjach, aby umożliwić ulepszenia wydajności. Obsługiwanym sposobem uzyskania odwołania do obiektu instancji komponentu jest dodanie właściwości, refktóra jest funkcją wywoływaną z instancją jako parametrem. Pozwala to również na dostęp do obiektów, które nie znajdują się na najwyższym poziomie, np. Jeśli renderujesz <MuiThemeProvider><Hello ref={setHelloRef} /></MuiThemeProvider>, otrzymujesz właściwe odniesienie przekazane do setHelloReffunkcji, a nie do MuiThemeProvider.
Periata Breatta

Uncaught TypeError: _react2.default.render nie jest funkcją
Partha Paul

46

Możesz polubić

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;

Teraz spoza tego komponentu możesz wywołać w ten sposób poniżej

window.helloComponent.alertMessage();

2
W rzeczywistości proste i funkcjonalne! Tak jak powinno być. Jestem pod wrażeniem tego, jakie to proste; naprawdę nie myślałem. Prawdopodobnie to podejście nie zadziała tak dobrze, jeśli jest coraz więcej komponentów, które powinieneś. Dziękuję Ci!
Leonardo Maffei

6
Dodanie zmiennej globalnej nie jest dobrym rozwiązaniem. Więcej informacji o tym, dlaczego zmienne globalne są złe, znajdziesz tutaj: wiki.c2.com/?GlobalVariablesAreBad
Salvatore Zappalà

4
Dzięki za głosowanie w dół !! Tak, zmienne globalne nie są dobre, ale to sposób na rozwiązanie problemu.
Kushal Jain

1
To jest właśnie pragmatyczne i proste rozwiązanie, którego potrzebowałem, dzięki!
Florent Destremau

2
zadziałało, ale nie zadziała, jeśli masz wiele tego samego komponentu na tej samej stronie.
gaurav

26

Zrobiłem coś takiego:

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

}

A potem gdzie indziej:

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

}

Nie testowałem dokładnie tego kodu, ale jest to podobne do tego, co zrobiłem w moim projekcie i działa dobrze :). Oczywiście jest to zły przykład, powinieneś po prostu użyć do tego rekwizytów, ale w moim przypadku pod-komponent wykonał wywołanie API, które chciałem zachować wewnątrz tego komponentu. W takim przypadku jest to fajne rozwiązanie.


5
Myślę, że miałeś na myślithis.cowCallbacks.say('moo')
Steven

Przy okazji, możliwe jest przekazanie thisdo wywołania zwrotnego (który będzie instancją Cow) zamiast callbacks.
WebBrother

@WebBrother Tak, ale to byłoby jeszcze bardziej
hakerskie

6

Ponieważ rendermetoda może być przestarzała zwracana wartość, zalecanym podejściem jest teraz dołączenie odwołania wywołania zwrotnego do elementu głównego. Lubię to:

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

do którego możemy następnie uzyskać dostęp za pomocą window.helloComponent, a każda z jego metod jest dostępna za pomocą window.helloComponent.METHOD.

Oto pełny przykład:

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>


Lepiej umieścić odniesienie w stanie lokalnym zamiast w obiekcie okna ... ref={component => {this.setState({ helloComponent: component; })}} Następnie w module obsługi kliknięcia ... this.state.helloComponent.alertMessage();
Bill Dagg

W nawiązaniu do mojego poprzedniego komentarza ... lub, jeszcze lepiej, po prostu ustaw metodę alertMessage składnika w stanie.
Bill Dagg

Kiedy próbuję tego, dostaję Warning: Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?i nie wiąże komponentu z oknem
Guerrilla

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

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

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

Możesz wywołać tę metodę z okna za pomocą window.alertMessage().


To by działało, ale dodanie zmiennej globalnej nie jest dobrym rozwiązaniem z wielu powodów. Więcej informacji o tym, dlaczego zmienne globalne są złe, można znaleźć tutaj: wiki.c2.com/?GlobalVariablesAreBad
Salvatore Zappalà

3

Jeśli pracujesz w ES6, po prostu użyj słowa kluczowego „static” w swojej metodzie z przykładu: static alertMessage: function() { ...
},

Mam nadzieję, że mogę pomóc każdemu :)


Nie możesz dotrzeć do właściwości lub stanu w funkcji statycznej.
Serdar Değirmenci,

Ok tak, ale pytanie dotyczyło dostępu do funkcji alertMessage (), aby można było użyć HelloElement.alertMessage ().
darmis

Generalnie wywołanie funkcji bez użycia właściwości i stanu nie ma żadnego efektu. Ale ponieważ masz rację co do pełnienia funkcji, wycofuję swój głos
Serdar Değirmenci,

3

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

Powodzenia...


2

Możesz po prostu dodać onClickprocedurę obsługi do elementu div za pomocą funkcji ( onClickjest to własna implementacja Reacta onClick) i możesz uzyskać dostęp do właściwości w{ } nawiasach klamrowych, a pojawi się komunikat ostrzegawczy.

W przypadku, gdy chcesz zdefiniować statyczne metody, które mogą być wywoływane w klasie komponentu - powinieneś użyć statyki. Mimo że:

Metody zdefiniowane w tym bloku są statyczne, co oznacza, że ​​można je uruchamiać przed utworzeniem jakichkolwiek instancji komponentów, a metody nie mają dostępu do właściwości ani stanu komponentów. Jeśli chcesz sprawdzić wartość właściwości w statycznej należy umieścić obiekt wywołujący w właściwościach jako argument metody statycznej. " ( źródło )

Przykładowy kod:

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

Mam nadzieję, że to ci trochę pomoże, ponieważ nie wiem, czy dobrze zrozumiałem twoje pytanie, więc popraw mnie, jeśli źle zinterpretowałem :)



2

metoda 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>

Metoda 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>

metoda 1 to bardzo łatwy i czysty sposób na dostęp do metod komponentów potomnych. dzięki!
gabdara

1

Używam tej metody pomocniczej do renderowania komponentów i zwracania instancji komponentu. Metody można wywołać w tej instancji.

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);
        });
    }
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.