Saturday, February 13, 2021

React tether-component - get access to renderTarget element

Problem

The renderTarget (anchor) is a div that displays a default color.  When the renderTarget is clicked, it opens up the renderElement, which is a react-color component.  

I need to be able to choose a new color with react-color and then display it on the anchor (renderTarget).  

Environment:

CentOS Linux release 7.3.1611 (Core)

React 17.1

react-tether 2.0.7

react-color 2.19.3

Solution

I wasn't able to find a way to get access to either the renderTarget or renderElement directly.  The references to those are internal to the <TetherComponent>.  To gain access to the renderTarget element, I added a React ref in the constructor to the <TetherComponent>:

this.tetherComponentRef 

and assigned it when defining the <TetherComponent>, and accessed with:

this.tetherComponentRef._tetherInstance.target

The full solution is here:

import React from "react";
import { CompactPicker } from "react-color";
import TetherComponent from "react-tether";

const defaultOptions = {
  attachment: "top left",
  targetAttachment: "top left"
};

class CherryShoeColorPicker extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      open: false
    };
    this.handleChange = this.handleChange.bind(this);
    this.handleClick = this.handleClick.bind(this);

    this.tetherComponentRef = React.createRef();
  }

  /**
   * Handler when the CompactPicker color is changed
* @param {Object} colorObject */ handleChange(colorObject) { const { open } = this.state; this.tetherComponentRef._tetherInstance.target.style.fill = colorObject.hex; this.setState({ open: !open }); } /** * Handler when the anchor element is clicked */ handleClick() { const { open } = this.state; this.setState({ open: !open }); } render() { const { color, tetherOptions } = this.props; const { open } = this.state; const tetherOptions = tetherOptions ? { ...defaultOptions, ...tetherOptions } : defaultOptions; return ( <div> <TetherComponent {...tetherOptions} ref={tether => (this.tetherComponentRef = tether)} // renderTarget: This is what the item will be tethered to, make sure to attach the ref renderTarget={ref => ( <div ref={ref} onClick={this.handleClick} style={{ backgroundColor: color, height: "10px", width: "10px" }} ></div> )} // renderElement: this item will be tethered to the the component returned by renderTarget renderElement={ref => open && ( <CompactPicker ref={ref} color={color} onChangeComplete={this.handleChange} /> ) } /> </div> ); } } CherryShoeColorPicker.defaultProps = { tetherOptions: {} }; export default CherryShoeColorPicker;

You can also pass in JSX as props for use in either the renderTarget or renderElement.  The key thing is that you need to pass the ref argument into the renderTarget/renderElement function, and that the ref needs to be attached to the highest element in the DOM tree.  The highest element must already be defined when defining <TetherComponent>, it cannot be passed in as JSX from a prop.

These articles were helpful:

https://www.npmjs.com/package/react-tether

https://reactjs.org/docs/refs-and-the-dom.html

https://github.com/danreeves/react-tether/issues/1

No comments:

Post a Comment

I appreciate your time in leaving a comment!