Martin Mazza Dawson Martin Mazza Dawson - 18 days ago 9
Javascript Question

React different ways of calling a child method

React says we should not use

refs
where possible and I noticed that you can't use shallow rendering testing with refs so I have tried to remove refs where possible. I have a child component like this:

class Child extends React.Component {
play = () => {
//play the media
},
pause = () => {
//pause the media
},
setMedia = (newMedia) => {
//set the new media
}
}


I then have a parent component that needs to call these methods. For the
setMedia
I can just use props with the
componentWillReceiveProps
and call
setMedia
when the new props come in to the child.

With the
play
and
pause
functions I cannot do this.

Ben Alpert replied to this post and said:


In general, data should be passed down the tree via props. There are a few exceptions to this (such as calling .focus() or triggering a one-time animation that doesn't really "change" the state) but any time you're exposing a method called "set", props are usually a better choice. Try to make it so that the inner input component worries about its size and appearance so that none of its ancestors do.


Which is the best way to call a child function?


  1. play()
    and
    pause()
    methods can be called from refs as they do not change the state just like
    focus()
    and use props for the other functions that have arguments.

  2. Call the child functions by passing the method name in although this just seems hacky and a lot more complex:

    class Child extends React.Component {
    play = () => {
    //play the media
    },
    pause = () => {
    //pause the media
    },
    setMedia = (newMedia) => {
    //set the new media
    },
    _callFunctions = (functions) => {
    if (!functions.length) {
    return;
    }

    //call each new function
    functions.forEach((func) => this[func]());

    //Empty the functions as they have been called
    this.props.updateFunctions({functions: []});
    }
    componentWillReceiveProps(nextProps) {
    this._callFunctions(nextProps.functions);
    }
    }

    class Parent extends React.Component {
    updateFunctions = (newFunctions) => this.setState({functions: newFunctions});
    differentPlayMethod = () => {
    //...Do other stuff
    this.updateFunctions("play");
    }
    render() {
    return (
    <Child updateFunctions={this.updateFunctions}/>
    );
    }
    }

  3. Do this in the child component:
    this.props.updateFunctions({play: this.play});

    The problem with this is that we are exposing(copying) a method to another component that shouldn't really know about it...



Which is the best way to do this?

I am using method number 2 at the moment and I don't really like it.

To override child functions I have also done something similar to above. Should I just use refs instead?

Answer

Rather than call child functions, try to pass data and functions down from the parent. Alongside your component, you can export a wrapper or higher order function that provides the necessary state / functions.

let withMedia = Wrapped => {
  return class extends React.Component {
    state = { playing: false }
    play() { ... }
    render() {
      return (
        <Wrapped 
          {...this.state} 
          {...this.props} 
          play={this.play}
        />
      )
    }
  }
}

Then in your parent component:

import { Media, withMedia } from 'your-library'

let Parent = props =>
  <div>
    <button onClick={props.play}>Play</button>
    <Media playing={props.playing} />
  </div>

export default withMedia(Parent)
Comments