Redux Selectors
Selectors provide a clean way to access and compute derived state from the Redux store. RemixWarp uses the reselect library for memoized selectors that optimize performance by preventing unnecessary re-computations.
Creating Selectors
Basic Selectors
Simple selectors directly access store state:
// src/selectors/project-state.js
import { createSelector } from 'reselect';
// Basic selectors
export const getProjectState = state => state.projectState;
export const getProjectId = state => state.projectState.projectId;
export const getProjectTitle = state => state.projectState.projectTitle;
export const getIsLoading = state => state.projectState.isLoading;
export const getSaveState = state => state.projectState.saveState;
Computed Selectors
Use createSelector for derived data that depends on multiple pieces of state:
// Computed selectors
export const getIsProjectLoaded = createSelector(
[getProjectId, getIsLoading],
(projectId, isLoading) => projectId !== null && !isLoading
);
export const getHasUnsavedChanges = createSelector(
[getSaveState],
(saveState) => saveState === 'NOT_SAVED'
);
export const getProjectStatus = createSelector(
[getIsLoading, getIsProjectLoaded, getHasUnsavedChanges],
(isLoading, isLoaded, hasChanges) => {
if (isLoading) return 'loading';
if (!isLoaded) return 'no-project';
if (hasChanges) return 'unsaved';
return 'saved';
}
);
VM Selectors
Target Management
// src/selectors/vm.js
export const getVM = state => state.vm;
export const getEditingTarget = state => state.vm.editingTarget;
export const getTargets = state => state.vm.targets;
export const getStage = state => state.vm.stage;
export const getMonitors = state => state.vm.monitors;
export const getEditingTargetObject = createSelector(
[getTargets, getEditingTarget],
(targets, editingTargetId) =>
targets.find(target => target.id === editingTargetId)
);
export const getSprites = createSelector(
[getTargets],
(targets) => targets.filter(target => !target.isStage)
);
export const getSpriteCount = createSelector(
[getSprites],
(sprites) => sprites.length
);
Complex Computations
For expensive computations, use memoized selectors:
export const getTargetSpriteCounts = createSelector(
[getTargets],
(targets) => {
// Expensive computation
return targets.reduce((counts, target) => {
if (!target.isStage) {
const sprite = target.sprite;
counts[sprite.name] = (counts[sprite.name] || 0) + 1;
}
return counts;
}, {});
}
);
GUI Selectors
Theme and Display
// src/selectors/gui.js
export const getGUI = state => state.gui;
export const getTheme = state => state.gui.theme.theme;
export const getThemeColors = state => state.gui.theme.colors;
export const getCustomTheme = state => state.gui.theme.customTheme;
export const getStageSize = state => state.gui.stage.stageSize;
export const getIsPlayerOnly = createSelector(
[getGUI],
(gui) => gui.mode.isPlayerOnly
);
export const getIsFullScreen = createSelector(
[getGUI],
(gui) => gui.mode.isFullScreen
);
export const getEffectiveTheme = createSelector(
[getTheme, getCustomTheme],
(theme, customTheme) => customTheme || theme
);
Mode and Layout
export const getLayoutInfo = createSelector(
[getGUI, getIsFullScreen, getStageSize],
(gui, isFullScreen, stageSize) => {
const baseLayout = {
stageSize,
isFullScreen
};
if (isFullScreen) {
return {
...baseLayout,
showToolbox: false,
showSprites: false
};
}
return {
...baseLayout,
showToolbox: true,
showSprites: true
};
}
);
Asset Selectors
Costumes and Sounds
// src/selectors/assets.js
export const getAssets = state => state.assets;
export const getDefaultProject = state => state.assets.defaultProject;
export const getCostumes = state => state.assets.costumes;
export const getSounds = state => state.assets.sounds;
export const getCostumesByTarget = createSelector(
[getCostumes, getTargets],
(costumes, targets) => {
const costumesByTarget = {};
targets.forEach(target => {
costumesByTarget[target.id] = costumes.filter(
costume => costume.targetId === target.id
);
});
return costumesByTarget;
}
);
export const getSoundsByTarget = createSelector(
[getSounds, getTargets],
(sounds, targets) => {
const soundsByTarget = {};
targets.forEach(target => {
soundsByTarget[target.id] = sounds.filter(
sound => sound.targetId === target.id
);
});
return soundsByTarget;
}
);