jordan
jordan

Reputation: 10722

React - Getting refs to wrapped class components

I have a map component that contains a child sidebar component. I am trying to do a relatively simple task of scrolling to the place in the list of places in the sidebar when it's map marker is clicked on. But, because the sidebar needs to be wrapped in withRouter and connect, I'm unable to set a ref (ref) => this.sidebar = ref in the map component.

export class Map extends React.Component {
  ...

  handleClick() {
    this.sidebar.scrollToPlace(place.id);
  }

  render () {
    return (
      <MapSidebar
        // unable to set ref
      />
    )
  }
}

and

class MapSidebar extends React.Component {
  ...

  scrollToPlace(id) {
    this.refs[id].scrollIntoView({block: 'end', behavior: 'smooth'});
  }
}
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(MapSidebar));

I know that using wrappedComponentRef could get me the contents of withRouter, but then I still have connect to deal with.

I also tried creating a custom ref on the MapSidebar instance:

<MapSidebar
  getReference={(ref) => {
    this.sidebar = ref;
  }} />

and then in the MapSidebar class constructor, calling:

if(this.props.getReference) {
  this.props.getReference(this);
}

but that resulted in an infinite loop of that component updating (although I'm not sure I understand why).

Is there a better way to get around these issues?

Upvotes: 2

Views: 1081

Answers (2)

nanobar
nanobar

Reputation: 66355

Store a reference in both classes:

// MapSidebar render - add this to the element you want.
<div ref={r => (this.ref = r)}>

Then in Map render:

<MapSidebar ref={r => (this.sidebar = r)}>

Now after Map has mounted you have access to the ref:

this.sidebar.ref

Upvotes: 1

klugjo
klugjo

Reputation: 20885

I suggest you avoid refs and simply pass the scroll value down:

export class Map extends React.Component {
  ...

  handleClick() {
    this.setState({scrollToPlaceId: place.id});
  }

  render () {
    return (
      <MapSidebar
        // Add a new property
        scrollToPlace={this.state.scrollToPlaceId}
      />
    )
  }
}

Then in your sidebar component, just listen to scroll changes in componentWillReceiveProps for example

class MapSidebar extends React.Component {
  ...

  componentWillReceiveProps(nextProps) {
    if (nextProps.scrollToPlace !== this.props.scrollToPlace) {
      this.refs[nextProps.scrollToPlace].scrollIntoView({block: 'end', behavior: 'smooth'});
    }
  }
}
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(MapSidebar));

Upvotes: 1

Related Questions