This issue is part of the larger effort to modernize the Jaeger UI codebase (Parent Issue #2610). The goal is to refactor the Node component from a React Class Component to a React Functional Component using Hooks.
Target Component: packages/plexus/src/Digraph/Node.tsx
Note: This component is part of the plexus package, which is a graph visualization library used by Jaeger UI.
Acceptance Criteria
Plexus-Specific Guidance
| Pattern |
Class Approach |
Functional Approach |
| Instance ID |
baseId = \prefix--${idCounter++}`` |
const baseId = useRef(\prefix--${idCounter++}`).current` |
| Memoization |
memoizeOne(fn) |
useMemo(() => fn, [deps]) or keep memoizeOne with useRef |
| Static Props |
static defaultProps = {...} |
Function parameter defaults or separate constant |
| Render Utils |
this.renderUtils = {...} |
const renderUtils = useMemo(() => ({...}), [deps]) |
Migration Guide (Cheat Sheet)
| Feature |
Old (Class) |
New (Functional) |
| State |
this.state = { isOpen: false } |
const [isOpen, setIsOpen] = useState(false); |
| Side Effects |
componentDidMount() |
useEffect(() => { ... }, []) |
| Props |
this.props.traceId |
props.traceId (or destructure: const { traceId } = props;) |
| Default Props |
static defaultProps = { name: 'Guest' } |
function Component({ name = 'Guest' }) { ... } |
| Performance |
class MyComp extends React.PureComponent |
export default React.memo(MyComp) |
| Context |
static contextType = MyContext |
const val = useContext(MyContext) |
| Refs |
this.myRef = React.createRef() |
const myRef = useRef(null) |
Common Pitfalls (Read Carefully!)
- Stale Closures: Be careful when using
useEffect. If you use a variable inside the effect that comes from props or state, it must be in the dependency array. If you omit it, the effect will use the old value.
- Testing with Enzyme: Functional components do not have instances. Tests like
wrapper.instance().method() or wrapper.state('value') will fail. You should refactor these tests to simulate events (clicks) and check the rendered output instead.
- Static Properties: Class components can have static properties attached directly. For functional components, you'll need to assign these after the function definition or use module-level constants.
Resources
This issue is part of the larger effort to modernize the Jaeger UI codebase (Parent Issue #2610). The goal is to refactor the
Nodecomponent from a React Class Component to a React Functional Component using Hooks.Target Component:
packages/plexus/src/Digraph/Node.tsxAcceptance Criteria
this.stateis replaced withuseStateoruseReducer.useEffect.memoizeOnecalls are converted touseMemowhere appropriate.React.memoto maintainPureComponentbehavior.Plexus-Specific Guidance
baseId = \prefix--${idCounter++}``const baseId = useRef(\prefix--${idCounter++}`).current`memoizeOne(fn)useMemo(() => fn, [deps])or keepmemoizeOnewithuseRefstatic defaultProps = {...}this.renderUtils = {...}const renderUtils = useMemo(() => ({...}), [deps])Migration Guide (Cheat Sheet)
this.state = { isOpen: false }const [isOpen, setIsOpen] = useState(false);componentDidMount()useEffect(() => { ... }, [])this.props.traceIdprops.traceId(or destructure:const { traceId } = props;)static defaultProps = { name: 'Guest' }function Component({ name = 'Guest' }) { ... }class MyComp extends React.PureComponentexport default React.memo(MyComp)static contextType = MyContextconst val = useContext(MyContext)this.myRef = React.createRef()const myRef = useRef(null)Common Pitfalls (Read Carefully!)
useEffect. If you use a variable inside the effect that comes from props or state, it must be in the dependency array. If you omit it, the effect will use the old value.wrapper.instance().method()orwrapper.state('value')will fail. You should refactor these tests to simulate events (clicks) and check the rendered output instead.Resources
connect().