Search code examples
reactjsooptypescriptencapsulation

React - interact with child component


I would like to know what is the best practice for parent-child component interaction and whether the following approach satisfies good practices.

Let's say that we have two components: Parent and Child, where Parent gets handlers on Child in a fashion similar to that of obtaining a ref on element.

class Parent extends React.Component<{}, {}> {

  private handlers: {
    Child: ChildHandlers
    /** And so on... **/
  };

  render() {
    return (
      <div>
        <Child handlers={(handlers: ChildHandlers) => { this.handlers.Child = handlers; }} />
        <button onClick={() => this.handlers.Child.toggle() /** Or change Parent.state and then trigger **/}>toggle from parent</button>
      </div>
    );
  }
}

class Child extends React.Component<ChildProps, ChildState> {
  constructor(props: ChildProps) {
    super(props);

    this.exposeHandlers();
  }

  private toggle(): void {
    this.setState({ visible: !this.state.visible });
  }

  private exposeHandlers() {
    let handlers: ChildHandlers = {
      toggle: () => this.toggle()
    };

    this.props.handlers(handlers);
  }

  render() {
    return (
      <div>
        { this.state.visible && (
          <div>
            <h2>I'm being toggled!</h2>
            <button onClick={() => this.toggle()} />toggle from child<button>
          </div>
        ) }
      </div>
    );
  }
}

It seems to be fine because:

  • It allows to be consistent
  • It's like exposing an interface used by both components without coupling them too much
  • It's keeping Child's state out of Parent's business to a reasonable extent
  • It's easier then to add callback to toggle method to perform some actions on Parent after updating Child's state.

But since I'm not experienced, are there any (even slightest) problems with that?


Solution

  • Seems like an overkill to me.

    What's wrong with exposing those methods and just having a reference in Parent to an instance of Child and then invoke those methods when needed?

    Parent:

    class Parent extends React.Component<{}, {}> {
        private child: Child;
    
        render() {
            return (
                <div>
                <Child ref={ el => this.child = el } />
                <button onClick={() => this.child.toggle()}>toggle from parent</button>
                </div>
            );
        }
    }
    

    Child:

    class Child extends React.Component<{}, ChildState> {
        public toggle(): void {
            this.setState({ visible: !this.state.visible });
        }
    
        render() {
            if (!this.state.visible) {
                return <div />;
            }
    
            return (
                <div>
                    <div>
                        <h2>I'm being toggled!</h2>
                        <button onClick={() => this.toggle()}>toggle from child</button>
                    </div>
                </div>
            );
        }
    }