Home > Software design >  In React, Javascript, how to convert object with SVG element parameters into actual SVG elements
In React, Javascript, how to convert object with SVG element parameters into actual SVG elements

Time:02-03

const courtStructure = [
    { class: 'legend-rect', shape: 'rect', x: 0, y: TLP   38, width: 50, height: 8, fill: headerStyle.backgroundColor }, // rectangle behind legend
    { class: 'backboard', shape: 'line', x1: 22, x2: 28, y1: 3.75, y2: 3.75, stroke: hoopColor, opacity: hoopOpacity, strokeWidth: 0.5 }, // backboard
    { class: 'rim', shape: 'circle', r: 0.95, cx: 25, cy: 5.1, opacity: hoopOpacity, fill: hoopColor }, // the circle part of rim
    { class: 'back-iron', shape: 'rect', x: 24.625, y: 3.95, width: 0.75, height: 0.5, stroke: 0, opacity: hoopOpacity, fill: hoopColor } // back iron on rim
];

<rect
    className='legend-rect'
    x={0}
    y={TLP   38}
    width={50}
    height={8}
    fill={headerStyle.backgroundColor}
/>
...

We have the courtStructure array of objects, and we would like to .map() over the array and create the SVG elements described. Example for the first rect is shown above. The keys in each object within courtStructure should be created as a field on the SVG element. The shape determines the element type.

The hardest part of achieving this seems to be:

  • creating the correct element (rect, line, circle, etc.)
  • handling the fact that different objects have different keys / parameters for the SVG

Is this possible to do?

CodePudding user response:

For each object extract the properties that need "name mapping", e.g. shape, class, etc. The remaining properties are kept in a single object and passed to React as spread attributes.

You can then create the component via dynamic tags or directly via React.createElement:

const components = courtStructure.map(
  ({shape: Shape, class: className, ...props}) => {
    return <Shape className={className} {...props} />
    // or
    return React.createElement(Shape, {className, ...props})
  }
)

Also see

CodePudding user response:

I would probably change how the object structure is set up to separate the shape from the actual attributes, and make the keys of the attributes match the attributes you pass to the element like this:

const courtStructure = [
    { 
        shape: 'rect', 
        attributes: {
            className: 'legend-rect', 
            x: 0, 
            y: TLP   38, 
            width: 50, 
            height: 8, 
            fill: headerStyle.backgroundColor 
        }
    }, // rectangle behind legend
    ...
];

This way you can map through the array and make a switch statement for the shape to decide what element to render, and destructure the attributes on the svg element. Something like this:

{courtStructure.map((element) => {
    switch(element.shape) {
        case 'rect':
            return (
                <rect {...element.attributes} />
            );
        case 'line':
            return (
                <line {...element.attributes} />
            );
        case 'circle':
            return (
                <circle {...element.attributes} />
            );
    }
})}

You could also obviously refactor this logic to its own Shape component to make it cleaner!

  • Related