1

I need to render a React child component from UV coordinates (normalized coordinates / U & V are typically in [0;1] range)

But I don't know how to get parent dimension during children rendering.

I would like to perform something like (using tsx):

const Child = (props: {u:Number, v:Number}) => 
      <circle cx={parentClientWidth*props.u} cy={parentClientHeight*props.v} r="5" fill="black"></circle>;

const Parent = () => 
      <svg>
         <Child u={0.3} v={0.5} />
      </svg>;

I woulder if using a context object could be the right way?...


const Child = (props: {u:Number, v:Number}) => {
      const workspace= useContext(WorkspaceContext);

      return <circle cx={workspace.width*u} cy={workspace.height*v} r="5"></circle>;
}

Note: In this simple case I could use percents for my cx and cy coordinates but my real case is much more complicated...

Charles HETIER
  • 1,934
  • 16
  • 28
  • 1
    You probably need to use state in the parent. Update the state in componentDidUpdate(). Then pass though into your Child – Tree Nguyen Aug 21 '19 at 12:17
  • so you are trying to get the svg width? – adel Aug 21 '19 at 12:37
  • @adel Yes exactly (SvgSvgElement.clientWidth), in the child at render time – Charles HETIER Aug 21 '19 at 12:45
  • @CharlesHETIER then check this post : https://stackoverflow.com/questions/43817118/how-to-get-the-width-of-a-react-element – adel Aug 21 '19 at 12:49
  • @adel, thanks a lot for the reference. Actually I do know how to get a react ref and from a ref, get the clientWidth of the ref. My question is more about life cycle and where is it the most judicious to fetch the dimension to recover them in children at render time – Charles HETIER Aug 21 '19 at 12:53

1 Answers1

0

After digging a lot the react documentation, I finally found a way that seems to me not too hacky... But I'm still learning so there might be another better way (regarding performances? readability?...) Anyway here is the idea of my current solution:

// Shares the dimensions of the workspace through children
const WorkspaceContext = createContext();

/** A child component.
 * It should be placed at specified (u,v) within the parent component. 
 */
const Child = (props: {u:Number, v:Number}) => {
     const workspaceContext = useContext(WorkspaceContext);
     return <circle cx={workspaceContext.width*props.u} cy={workspaceContext.height*props.v} r="5" fill="black"></circle>;
};

/** The parent SVG component */
const Parent = () => {
      const [dimensions, setDimensions] = useState({
            width: undefined, 
            height:undefined, 
            outdated: true // Triggers a re render when changed
       });

      // I'm using the ref callback to get the svg dimensions
      const parentRefCallback = (element: SVGSVGElement) => {
        if(element) {
            const width = element.clientWidth;
            const height = element.clientHeight;
            if(dimensions.width !== width || dimensions.height !== height) {
                setDimensions({width, height, outdated: false});
            }
        }
    };

    useEffect(() => {
        const handler = () => setDimensions({...dimensions, outdated: true}); // re renders!
        window.addEventListener('resize', handler);
        return () => window.removeEventListener('resize', handler);
    }, [])

      return <svg ref={parentRefCallback}>
         <WorkspaceContext.Provider value={dimensions}>
             <Child u={0.3} v={0.5} />
         </WorkspaceContext.Provider>
      </svg>;
}
Charles HETIER
  • 1,934
  • 16
  • 28