New to react and styled-components and have probably got myself in a muddle through not understanding how it all works.
Let's start from the top. I have a simple page (App.js) that renders two components "Knobs". I want to pass each 'Knob' one or more properties so it can calculate its size and other relevant instance props. In the example below, one know is 200px in size, and it's sister is a 100px.
import React from 'react';
import Knob from './components/knob.js'
import './App.css';
function App() {
return (
<div className="App">
<header className="App-header">
hello world
<Knob size={200} />
<Knob size={100} />
</header>
</div>
);
}
export default App;
So far so good. Now inside the Knob component, I do all my transformations and ultimately have a scaled Knob. The knob is a svg based component (abbreviated below but still long, sorry).
So - the good news is that it all works! But I know I am approaching this wrong.
In order to get it to work and use the this.state.size to calculate the appropriate font size for the component , I had to move the styled-component object into the class...and create an empty declaration outside the class (Styles).
So - my ask is two-fold:
- I think my approach is philosophically damaged...and would love experts here to unscramble my brain.
- How would you edit the code to make it not just work, but work right!
a) It seems to me that the entire Styles declaration belongs outside the class. b) No idea why I have to reference this.state.xxxx twice c) I think I am also mixing up the use of props and state.
Other than that it's perfect (:. But -- as you see from the screenshot below...it actually works.
Ugh.
import React from 'react'
import { Knob, Pointer, Value, Scale, Arc } from 'rc-knob'
import styled from 'styled-components';
// this is my weird hack to get things working. Declare Styles outside of the class.
var Styles = {}
export default class MyKnob extends React.Component {
constructor(props) {
super(props)
this.state = {
size: props.size,
value: props.value,
radius: (props.value/2).toString(),
fontSize: (props.size * .2)
}
//Now define styles inside the class and i can use the fontsize that is derived from the size passed by the parent component!
Styles = styled.div`
.vpotText {
fill: green;
font-size: ${this.state.fontSize+'px'};
}
`
}
// no idea why I need this block....but without it I get a whole bunch of
// error TS2339: Property 'value' does not exist on type 'Readonly<{}>'.
state = {
value: 50,
size: 100,
radius: '50',
fontSize: 12
}
static defaultProps = { value: 50, size: 100};
render(){
const customScaleTick = ({}) //abbreviated for readability.
return (
<Styles>
<Knob size={this.state.size}
angleOffset={220}
angleRange={280}
steps={10}
min={0}
max={100}
// note use of this.state.value to set parameters that affect the sizing/display of the component
value={this.state.value}
onChange={value => console.log(value)}
>
<Scale steps={10} tickWidth={1} tickHeight={2} radius={(this.state.size/2)*0.84} color='grey' />
<Arc arcWidth={2} color="#4eccff" background="#141a1e" radius = {(this.state.size/2)*0.76} />
<defs>
{/* GRADIENT DEFINITIONS REMOVED FOR READABILITY */}
</defs>
{/* NOTE: EXTENSIVE USE OF this.state.size TO ENSURE ALL PARTS OF THE COMPONENT ARE SCALED NICELY */}
<circle cx={this.state.size/2} cy={this.state.size/2} rx={(this.state.size/2)*0.8} fill = "url(#grad-dial-soft-shadow)" />
<ellipse cx={this.state.size/2} cy={(this.state.size/2)+2} rx={(this.state.size/2)*0.7} ry={(this.state.size/2)*0.7} fill='#141a1e' opacity='0.15' ></ellipse>
<circle cx={this.state.size/2} cy={this.state.size/2} r={(this.state.size/2)*0.7} fill = "url(#grad-dial-base)" stroke='#242a2e' strokeWidth='1.5'/>
<circle cx={this.state.size/2} cy={this.state.size/2} r={(this.state.size/2)*0.64} fill = 'transparent' stroke='url(#grad-dial-highlight)' strokeWidth='1.5'/>
<Pointer width={(this.state.size/2)*0.05} radius={(this.state.size/2)*0.47} type="circle" color='#4eccff' />
{/* THIS IS THE TRICKY ONE! */}
{/* IN ORDER TO GET THE FONT SIZE RIGHT ON THIS ELEMENT (svg) I NEED THE STYLE */}
<Value
marginBottom={(this.state.size-(this.state.fontSize)/2)/2}
className="vpotText"
/>
</Knob>
</Styles>
)}
}
here's a pic of the output:
