Skip to content
7 changes: 3 additions & 4 deletions packages/core/useClass/useClass.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {useRef} from 'react';
import {useState} from 'react';

/**
* Creates one instance of the class, `Ctor` with the provided `args`, for the life of the
Expand Down Expand Up @@ -29,10 +29,9 @@ import {useRef} from 'react';
* @private
*/
function useClass (Ctor, ...args) {
const ref = useRef(null);
ref.current = ref.current || new Ctor(...args);
const [state] = useState(() => new Ctor(...args));

return ref.current;
return state;
}

export default useClass;
Expand Down
9 changes: 6 additions & 3 deletions packages/core/useHandlers/tests/useHandlers-specs.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import '@testing-library/jest-dom';
import {render} from '@testing-library/react';
import {useEffect} from 'react';

import useHandlers from '../useHandlers';

Expand All @@ -17,9 +18,11 @@ describe('useHandlers', () => {
}
}, props, context);

data = {
handlers
};
useEffect(() => {
data = {
handlers
};
}, [handlers]);

return (
<div {...props} data-testid="divComponent" />
Expand Down
28 changes: 26 additions & 2 deletions packages/core/util/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,12 @@
* @exports perfNow
* @exports mapAndFilterChildren
* @exports shallowEqual
* @exports usePrevious
*/
import always from 'ramda/src/always';
import isType from 'ramda/src/is';
import unless from 'ramda/src/unless';
import {Children} from 'react';
import {Children, useState} from 'react';
import * as ReactIs from 'react-is';

import Job from './Job';
Expand Down Expand Up @@ -329,6 +330,28 @@ const shallowEqual = (a, b) => {
return true;
};

/**
* A custom hook that returns the previous value of a variable.
*
* @function
* @param {*} value The value to track.
*
* @returns {*} The value from the previous render.
* @memberof core/util
* @public
*/
const usePrevious = (value) => {
const [previousTrackedValue, setPreviousTrackedValue] = useState(value);
const [previousValue, setPreviousValue] = useState(value);

if (value !== previousTrackedValue) {
setPreviousTrackedValue(value);
setPreviousValue(previousTrackedValue);
}

return previousValue;
};

export {
cap,
clamp,
Expand All @@ -342,5 +365,6 @@ export {
perfNow,
mapAndFilterChildren,
setDefaultProps,
shallowEqual
shallowEqual,
usePrevious
};
2 changes: 1 addition & 1 deletion packages/sampler/stories/default/Routable.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ const HomeView = () => (
<Heading style={viewStyle}>/app/home</Heading>
);

const SettingModuleComponents = {};
// eslint-disable-next-line enact/display-name
const SettingModuleView = (settingModuleName) => () => (
<div style={viewStyle}>
Expand All @@ -65,6 +64,7 @@ export default {
};

export const Routable_ = () => {
const SettingModuleComponents = {};
let [path, setPath] = useState('/app');
const handleNavigate = (ev) => {
action('onNavigate')(ev);
Expand Down
4 changes: 2 additions & 2 deletions packages/spotlight/Spottable/Spottable.js
Original file line number Diff line number Diff line change
Expand Up @@ -145,10 +145,10 @@ const Spottable = hoc(defaultConfig, (config, Wrapped) => {
}
rest.tabIndex = tabIndex;

const handlers = useHandlers(spotHandlers, rest, spot);

delete rest.spotlightId;

const handlers = useHandlers(spotHandlers, rest, spot);

return (
<WrappedWithRef
{...rest}
Expand Down
18 changes: 10 additions & 8 deletions packages/spotlight/Spottable/useSpottable.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import useClass from '@enact/core/useClass';
import {useLayoutEffect, useRef} from 'react';
import {useLayoutEffect, useState} from 'react';

import {SpottableCore, spottableClass} from './SpottableCore';

Expand Down Expand Up @@ -56,22 +56,24 @@ const REMOTE_OK_KEY = 16777221;

const useSpottable = ({emulateMouse, getSpotRef, selectionKeys = [ENTER_KEY, REMOTE_OK_KEY], spotlightDisabled, ...props} = {}) => {
const hook = useClass(SpottableCore, {emulateMouse});
const context = useRef({
const [context, setContext] = useState({
prevSpotlightDisabled: spotlightDisabled,
spotlightDisabled
});

context.current = {
prevSpotlightDisabled: context.current.spotlightDisabled,
spotlightDisabled
};

let attributes = {};
if (props.spotlightId) {
attributes['data-spotlight-id'] = props.spotlightId;
}

hook.setPropsAndContext({selectionKeys, spotlightDisabled, ...props}, context.current);
if (context.spotlightDisabled !== spotlightDisabled) {
setContext({
prevSpotlightDisabled: context.spotlightDisabled,
spotlightDisabled
});
}

hook.setPropsAndContext({selectionKeys, spotlightDisabled, ...props}, context);

useLayoutEffect(() => {
hook.load(getSpotRef() || null);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {render} from '@testing-library/react';
import {useEffect} from 'react';

import AnnounceDecorator from '../AnnounceDecorator';

Expand All @@ -7,7 +8,9 @@ describe('AnnounceDecorator', () => {

// no-op wrapper
const Div = (props) => {
announceProps = props;
useEffect(() => {
announceProps = props;
}, [props]);

return <div />;
};
Expand Down
6 changes: 5 additions & 1 deletion packages/ui/AnnounceDecorator/tests/useAnnounce-specs.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
import {render, screen} from '@testing-library/react';
import {useEffect} from 'react';

import useAnnounce from '../useAnnounce';

describe('useAnnounce', () => {
let announceType;

function Base ({children, announce}) {
announceType = typeof announce;
useEffect(() => {
announceType = typeof announce;
}, [announce]);

return <div data-testid="announce">{children}</div>;
}

Expand Down
6 changes: 5 additions & 1 deletion packages/ui/Changeable/tests/Changeable-specs.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import '@testing-library/jest-dom';
import {act, render, screen} from '@testing-library/react';
import {useEffect} from 'react';

import Changeable from '../Changeable';

Expand All @@ -8,7 +9,10 @@ describe('Changeable', () => {
const testValue = 3;

const DivComponent = (props) => {
data = props;
useEffect(() => {
data = props;
}, [props]);

return <div data-testid="changeable">{props.value?.toString()}</div>;
};

Expand Down
9 changes: 7 additions & 2 deletions packages/ui/Measurable/tests/Measurable-specs.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
import {act, render} from '@testing-library/react';
import {useEffect} from 'react';

import Measurable, {useMeasurable} from '../Measurable';

let data;

const DivComponent = (props) => {
data = props;
const {controlsRef} = props;

return <div ref={props.controlsRef} />;
useEffect(() => {
data = props;
}, [props]);

return <div ref={controlsRef} />;
};

const MeasurableComponent = Measurable({refProp: 'controlsRef', measurementProp: 'controlsMeasurements'}, DivComponent);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
import '@testing-library/jest-dom';
import {render} from '@testing-library/react';
import {useEffect} from 'react';

import {PlaceholderControllerDecorator} from '../../Placeholder';

describe('PlaceholderControllerDecorator', () => {
let data;

const DivComponent = (props) => {
data = props;
useEffect(() => {
data = props;
}, [props]);

return <div data-testid="component" {...props} />;
};
Expand Down
5 changes: 4 additions & 1 deletion packages/ui/Resizable/tests/Resizable-specs.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
import Registry from '@enact/core/internal/Registry';
import {render} from '@testing-library/react';
import {useEffect} from 'react';

import Resizable, {ResizeContext} from '../Resizable';

describe('Resizable', () => {
let data;

const DivComponent = (props) => {
data = props;
useEffect(() => {
data = props;
}, [props]);

return <div data-testid="component" />;
};
Expand Down
5 changes: 4 additions & 1 deletion packages/ui/Resizable/tests/useResize-specs.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import Registry from '@enact/core/internal/Registry';
import {render} from '@testing-library/react';
import {useEffect} from 'react';

import {ResizeContext} from '../Resizable';
import useResize from '../useResize';
Expand All @@ -8,7 +9,9 @@ describe('useResize', () => {
let data;

const DivComponent = (props) => {
data = props;
useEffect(() => {
data = props;
}, [props]);

return <div data-testid="component" />;
};
Expand Down
11 changes: 9 additions & 2 deletions packages/ui/Skinnable/tests/Skinnable-specs.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import '@testing-library/jest-dom';
import {render, screen} from '@testing-library/react';
import {useEffect} from 'react';

import Skinnable from '../Skinnable';

Expand All @@ -9,7 +10,10 @@ describe('Skinnable Specs', () => {
let data;

const Component = (props) => {
data = props;
useEffect(() => {
data = props;
}, [props]);

return <div {...props} />;
};

Expand Down Expand Up @@ -77,7 +81,10 @@ describe('Skinnable Specs', () => {
let data;

const Component = (props) => {
data = props;
useEffect(() => {
data = props;
}, [props]);

return <div {...props} />;
};

Expand Down
6 changes: 4 additions & 2 deletions packages/ui/Skinnable/useSkins.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {createContext, use, useCallback, useMemo} from 'react';
import {createContext, use, useCallback, useMemo, useState} from 'react';

import {determineSkin, determineVariants, getClassName} from './util';

Expand Down Expand Up @@ -84,8 +84,10 @@ function useSkins (config) {

const {parentSkin, parentVariants} = use(SkinContext) || {};

const currentParentVariants = useMemo(() => parentVariants, [parentVariants]);

const effectiveSkin = determineSkin(defaultSkin, skin, parentSkin);
const effectiveVariants = useMemo(() => determineVariants(defaultVariants, variants, skinVariants, parentVariants), [defaultVariants, variants, skinVariants, parentVariants]);
const effectiveVariants = useMemo(() => determineVariants(defaultVariants, variants, skinVariants, currentParentVariants), [currentParentVariants, parentVariants, skinVariants, variants]);
const className = getClassName(skins, effectiveSkin, effectiveVariants);
const value = useMemo(() => ({parentSkin: effectiveSkin, parentVariants: effectiveVariants}), [effectiveSkin, effectiveVariants]);

Expand Down
4 changes: 2 additions & 2 deletions packages/ui/Toggleable/Toggleable.js
Original file line number Diff line number Diff line change
Expand Up @@ -180,8 +180,8 @@ const ToggleableHOC = hoc(defaultConfig, (config, Wrapped) => {
// either null or undefined. The ternary below enforces that but we don't want to
// continue this exception in the future and should sunset it with this HOC.
const {current: instance} = useRef({selected: null});
const selected = (instance.selected && propSelected == null) ? false : hook.selected;
instance.selected = propSelected;
const selected = (instance.selected && propSelected == null) ? false : hook.selected; // eslint-disable-line react-hooks/refs
instance.selected = propSelected; // eslint-disable-line react-hooks/refs

if (prop) {
updated[prop] = selected;
Expand Down
6 changes: 5 additions & 1 deletion packages/ui/Toggleable/tests/Toggleable-specs.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
import '@testing-library/jest-dom';
import {act, render, screen} from '@testing-library/react';
import {useEffect} from 'react';

import Toggleable from '../Toggleable';

describe('Toggleable', () => {
let data;

const DivComponent = (props) => {
data = props;
useEffect(() => {
data = props;
}, [props]);

return <div data-testid="selected-state">{props.selected?.toString()}</div>;
};

Expand Down
6 changes: 5 additions & 1 deletion packages/ui/Touchable/tests/Touchable-specs.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {act, createEvent, fireEvent, render, screen} from '@testing-library/react';
import {useEffect} from 'react';

import {configure, getConfig, resetDefaultConfig} from '../config';
import Touchable from '../Touchable';
Expand All @@ -14,7 +15,10 @@ describe('Touchable', () => {
let data;

const DivComponent = ({children = 'Toggle', id, onClick, onMouseDown, onMouseEnter, onMouseLeave, onMouseMove, onMouseUp, onTouchStart, onTouchEnd, ...props}) => {
data = props;
useEffect(() => {
data = props;
}, [props]);

return (
<div
data-testid="component"
Expand Down
Loading