3

My page contains two completely separate React components (different files, different classes, no parent-child relationship).

How can one component call an instance method in another component? The problem seems to be obtaining the instance of the target component.

EDIT: Both components share the same parent (i.e. they are rendered in the same render() method) but I still don't know how to pass the reference of the target component to the calling component.

Barry Fruitman
  • 12,316
  • 13
  • 72
  • 135
  • 1
    This is anti-pattern in react, suggestion would be place method in a containing component (parent to each) then pass to both of them as a prop. Alternative, you can create higher-order component which has this functionality, and "enhance" both components w/ it – aarosil Jul 06 '17 at 04:13
  • https://stackoverflow.com/questions/29642758/react-js-flux-vs-global-event-bus could help – ale Jul 06 '17 at 04:45

4 Answers4

4

The short answer is: they don't.

It's not clear what you're trying to accomplish, so I can't speak to the specifics of your case, but the way React components "communicate" with one another is via state and props. For example, consider a Page component that has two child components, CompA and CompB, rendered something like this:

<Page>
    <CompA />
    <CompB />
</Page>

If CompA needs to pass something to CompB, this is done through state on the Page component, with that state exposed as props on CompA and CompB, something like this:

class Page extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            sharedValue: 42,
        };
    }

    onChangeSharedValue(newValue) {
        this.setState({ sharedValue: newValue });
    }

    render() {
        return (
            <div>
                <CompA
                    sharedValue={this.state.sharedValue}
                    onChange={this.onChangeSharedValue}
                />
                <CompB
                    sharedValue={this.state.sharedValue}
                    onChange={this.onChangeSharedValue}
                />
            </div>
        );
    }
}

If CompA needs to change the shared value, it calls the onChange handler, which will change the state on the Page component. That value will then be propagated down to the CompB component.

There is no direct communication between components like you're describing; it is all done via state and props.

Kryten
  • 15,230
  • 6
  • 45
  • 68
  • You're right. My solution was to dispatch the same action in both components instead of trying to get them to talk to each other. – Barry Fruitman Jul 06 '17 at 21:25
4

"Props down, Events up."

If you provide us a specific example of what you're looking for, I can update this post with a more specific response.

But in general, there are a couple of strategies that you can take. Some of them are presented here.

The preferred approach is to simply move your calling method to the parent component. It's a common strategy in React.

If you're not able to, then the next step would be to write an event handler for the parent, and then pass this event down to the first child component.

Use this event to pass information up to the parent, so that when it gets triggered, data can be passed as props down to the second component.

Denny
  • 744
  • 4
  • 10
2

I only recently started doing React development and I found a solution for this problem that suits me. Admittedly, I haven't seen it referenced anywhere and when I showed it to a colleague who's been doing React for years, he kinda furrowed his brow and felt that it wasn't "right", but he couldn't really articulate to me why it's "wrong". I'm sure I'll be shouted down for it here, but I thought I'd share anyway:

File #1: objects.js

let objects= {};
export default objects;

File #2: firstComponent.js

import React from 'react';
import objects from 'objects';

class FirstComponent extends React.Component {
    constructor(props) {
        super(props);
        objects['FirstComponent'] = this; // store a reference to this component in 'objects'
    }

    doSomethingInFirstComponent() {
        console.log('did something in first component');
    }

    render() {
        return (<div></div>);
    }
}
export default FirstComponent;

File #3: secondComponent.js

import React from 'react';
import objects from 'objects';

class SecondComponent extends React.Component {
    render() {
        objects.FirstComponent.doSomethingInFirstComponent(); // call the method on the component referred to in 'objects'
        return (<div></div>);
    }
}
export default SecondComponent ;

When SecondComponent renders, it will trigger the console.log() in FirstComponent.doSomethingInFirstComponent(). This assumes, of course, that FirstComponent is actually mounted.

The "React Guys" that I know seem to think this approach is somehow evil. It uses a simple JavaScript object outside the normal React scope to maintain a reference to any existing objects that I choose to store there. Other than them telling me that "this isn't the way you do things in React", I haven't yet found a good explanation for how this will break or otherwise screw-up my app. I use it as a low-grade replacement for massive-overkill state-management tools like Redux. I also use it to avoid having to pass properties down through dozens of layers of React components just so something at the last level can trigger something waaaaay up in the first level.

That's not to say this approach doesn't have it's problems:

  1. It creates an obvious dependency between the generic objects object, any component that is designed to store a reference to itself inside objects, and any component that wishes to utilizes those references. Then again, using any kind of global state-management solution creates a similar dependency.
  2. It's probably a bad solution if you have any doubt that FirstComponent will be mounted before you try to call it from within SecondComponent.
  3. I've found that just having the reference to a React component won't allow you to do all the things that React components can do natively. For example, it won't work to call objects.FirstComponent.setState(). You can call a method in FirstComponent, which in turn can invoke its own setState(), but you can't invoke FirstComponent's setState() directly from within SecondComponent. Quite frankly, I think this is a good thing.
  4. You can, however, directly access the state values from the components referenced in objects.
  5. This should only be done with "global" components (components that functionally serve as singletons). If, for example, you had a simple UI component called BasicSpan that did little more than render a basic span tag, and you proceeded to use that component over and over again throughout your React app, I'm sure it would quickly become an unmanageable nightmare to try to place references to these simple components in the objects object and then try to intelligently manage calls to those components' internal methods.
0

you can send an event as props and call it from other component.

Say you have a class

    Class A{

    handleChange(evt)
    {

   this.setState({
name:evt.target.value

})
    }

    render{
    return(
    <div>
    <ComponentB name={this.state.name}{ onChange={this.handleChange}/>

    </div>

    );
    }
    }

Child Component

 Class B{

    handleChange()
    {

    //logic
    }

    render{
    return(
    <div>
    <input type="text" onChange={this.props.onChange}/>
    {this.props.name}
    </div>

    );

    }
  • Here in Component B when you change the input it will call the method of class A and update state of A.
  • Now getting the updated state as props in component B will give you the changed text that you just entered
Vikram Saini
  • 2,713
  • 1
  • 16
  • 33