refactor(29-01): wrap context consumers with React.memo

Components wrapped:
- SessionTrackGain: volume slider
- JKSessionMyTrack: local track display
- JKSessionVolumeModal: volume popup
- JKSessionPanModal: pan control popup
- JKSessionBackingTrack: backing track display
- JKSessionMetronome: metronome controls

Each component:
- Wrapped with memo(function ComponentName(...))
- Added displayName for debugging
- Preserved PropTypes

React.memo prevents re-renders when props haven't changed.
Combined with memoized context values, this eliminates
unnecessary re-renders across the component tree.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Nuwan 2026-03-05 17:53:34 +05:30
parent 566a53fb2b
commit b27ea854f4
6 changed files with 30 additions and 18 deletions

View File

@ -1,4 +1,4 @@
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect, memo } from 'react';
import { useMixersContext } from '../../context/MixersContext'; import { useMixersContext } from '../../context/MixersContext';
import { useJamClient } from '../../context/JamClientContext'; import { useJamClient } from '../../context/JamClientContext';
import usePanHelpers from '../../hooks/usePanHelpers'; import usePanHelpers from '../../hooks/usePanHelpers';
@ -12,7 +12,7 @@ import { faTimes } from '@fortawesome/free-solid-svg-icons';
import './JKSessionMyTrack.css'; import './JKSessionMyTrack.css';
import backingTrackIcon from "../../icons/instruments/icon_instrument_default.svg" import backingTrackIcon from "../../icons/instruments/icon_instrument_default.svg"
const JKSessionBackingTrack = ({ const JKSessionBackingTrack = memo(function JKSessionBackingTrack({
backingTrack, backingTrack,
mixers, mixers,
onClose onClose
@ -145,6 +145,8 @@ const JKSessionBackingTrack = ({
)} )}
</div> </div>
); );
}; });
JKSessionBackingTrack.displayName = 'JKSessionBackingTrack';
export default JKSessionBackingTrack; export default JKSessionBackingTrack;

View File

@ -1,4 +1,4 @@
import React from 'react'; import React, { memo } from 'react';
import { useMixersContext } from '../../context/MixersContext'; import { useMixersContext } from '../../context/MixersContext';
import { useJamClient } from '../../context/JamClientContext'; import { useJamClient } from '../../context/JamClientContext';
import SessionTrackVU from './SessionTrackVU'; import SessionTrackVU from './SessionTrackVU';
@ -6,7 +6,7 @@ import SessionTrackGain from './SessionTrackGain';
import './JKSessionMyTrack.css'; import './JKSessionMyTrack.css';
import computerIcon from '../../assets/img/instruments/icon_instrument_computer45_inverted.svg' import computerIcon from '../../assets/img/instruments/icon_instrument_computer45_inverted.svg'
const JKSessionMetronome = ({ const JKSessionMetronome = memo(function JKSessionMetronome({
mixers, mixers,
onClose onClose
}) => { }) => {
@ -112,6 +112,8 @@ const JKSessionMetronome = ({
</div> </div>
</div> </div>
); );
}; });
JKSessionMetronome.displayName = 'JKSessionMetronome';
export default JKSessionMetronome; export default JKSessionMetronome;

View File

@ -1,4 +1,4 @@
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect, memo } from 'react';
import { useDispatch, useSelector } from 'react-redux'; import { useDispatch, useSelector } from 'react-redux';
import { useMixersContext } from '../../context/MixersContext'; import { useMixersContext } from '../../context/MixersContext';
import { useJamClient } from '../../context/JamClientContext'; import { useJamClient } from '../../context/JamClientContext';
@ -20,7 +20,7 @@ import './JKSessionMyTrack.css';
import pluginIcon from '../../assets/img/client/plugin.svg'; import pluginIcon from '../../assets/img/client/plugin.svg';
import JKInstrumentIcon from '../profile/JKInstrumentIcon'; import JKInstrumentIcon from '../profile/JKInstrumentIcon';
const JKSessionMyTrack = ({ const JKSessionMyTrack = memo(function JKSessionMyTrack({
track, track,
mixers, mixers,
hasMixer, hasMixer,
@ -341,6 +341,8 @@ const JKSessionMyTrack = ({
)} )}
</div> </div>
); );
}; });
JKSessionMyTrack.displayName = 'JKSessionMyTrack';
export default JKSessionMyTrack; export default JKSessionMyTrack;

View File

@ -1,4 +1,4 @@
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect, memo } from 'react';
import { import {
Modal, Modal,
ModalHeader, ModalHeader,
@ -11,7 +11,7 @@ import {
import usePanHelpers from '../../hooks/usePanHelpers'; import usePanHelpers from '../../hooks/usePanHelpers';
import { useMixersContext } from '../../context/MixersContext'; import { useMixersContext } from '../../context/MixersContext';
const JKSessionPanModal = ({ isOpen, toggle, mixers }) => { const JKSessionPanModal = memo(function JKSessionPanModal({ isOpen, toggle, mixers }) {
const { convertPanToPercent, convertPercentToPan } = usePanHelpers(); const { convertPanToPercent, convertPercentToPan } = usePanHelpers();
const mixerHelper = useMixersContext(); const mixerHelper = useMixersContext();
const [panPercent, setPanPercent] = useState(50); // Center position const [panPercent, setPanPercent] = useState(50); // Center position
@ -99,6 +99,8 @@ const JKSessionPanModal = ({ isOpen, toggle, mixers }) => {
</ModalFooter> </ModalFooter>
</Modal> </Modal>
); );
}; });
JKSessionPanModal.displayName = 'JKSessionPanModal';
export default JKSessionPanModal; export default JKSessionPanModal;

View File

@ -1,4 +1,4 @@
import React, { useEffect, useState, useCallback } from 'react'; import React, { useEffect, useState, useCallback, memo } from 'react';
import { import {
Modal, Modal,
ModalHeader, ModalHeader,
@ -10,7 +10,7 @@ import { useMixersContext } from '../../context/MixersContext';
import { useJamClient } from '../../context/JamClientContext'; import { useJamClient } from '../../context/JamClientContext';
import { MIX_MODES, ChannelGroupIds, CategoryGroupIds } from '../../helpers/globals.js'; import { MIX_MODES, ChannelGroupIds, CategoryGroupIds } from '../../helpers/globals.js';
const JKSessionVolumeModal = ({ isOpen, toggle }) => { const JKSessionVolumeModal = memo(function JKSessionVolumeModal({ isOpen, toggle }) {
const mixerHelper = useMixersContext(); const mixerHelper = useMixersContext();
const jamClient = useJamClient(); const jamClient = useJamClient();
const mixers = mixerHelper.myTracks[0]?.mixers; const mixers = mixerHelper.myTracks[0]?.mixers;
@ -141,6 +141,8 @@ const JKSessionVolumeModal = ({ isOpen, toggle }) => {
</ModalBody> </ModalBody>
</Modal> </Modal>
); );
}; });
JKSessionVolumeModal.displayName = 'JKSessionVolumeModal';
export default JKSessionVolumeModal; export default JKSessionVolumeModal;

View File

@ -1,10 +1,10 @@
import React, { useState, useRef, useEffect, useLayoutEffect, useCallback } from 'react'; import React, { useState, useRef, useEffect, useLayoutEffect, useCallback, memo } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import './VolumeSlider.css'; // Keep the same CSS for now import './VolumeSlider.css'; // Keep the same CSS for now
import { useMixersContext } from '../../context/MixersContext'; import { useMixersContext } from '../../context/MixersContext';
import useFaderHelpers from '../../hooks/useFaderHelpers'; import useFaderHelpers from '../../hooks/useFaderHelpers';
const SessionTrackGain = ({ mixers, gainType, controlGroup, sessionController, orientation = 'vertical' }) => { const SessionTrackGain = memo(function SessionTrackGain({ mixers, gainType, controlGroup, sessionController, orientation = 'vertical' }) {
const mixerHelper = useMixersContext(); const mixerHelper = useMixersContext();
const faderHelpers = useFaderHelpers(); const faderHelpers = useFaderHelpers();
@ -177,7 +177,9 @@ const SessionTrackGain = ({ mixers, gainType, controlGroup, sessionController, o
</div> </div>
</div> </div>
); );
}; });
SessionTrackGain.displayName = 'SessionTrackGain';
SessionTrackGain.propTypes = { SessionTrackGain.propTypes = {
mixers: PropTypes.object, mixers: PropTypes.object,