Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Components: add useUpdateEffect to exhaustive-deps lint check #45771

Open
wants to merge 8 commits into
base: trunk
Choose a base branch
from
5 changes: 4 additions & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -349,7 +349,10 @@ module.exports = {
files: [ 'packages/components/src/**/*.[tj]s?(x)' ],
excludedFiles: [ ...developmentFiles ],
rules: {
'react-hooks/exhaustive-deps': 'error',
'react-hooks/exhaustive-deps': [
'error',
{ additionalHooks: 'useUpdateEffect' },
],
},
},
{
Expand Down
2 changes: 2 additions & 0 deletions packages/components/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@
- `LinkedButton`: remove unnecessary `span` tag ([#46063](https://github.com/WordPress/gutenberg/pull/46063))
- NumberControl: refactor styles/tests/stories to TypeScript, replace fireEvent with user-event ([#45990](https://github.com/WordPress/gutenberg/pull/45990)).
- `useBaseField`: Convert to TypeScript ([#45712](https://github.com/WordPress/gutenberg/pull/45712)).
- Add `useUpdateEffect` hook to the previously added `react-hooks/exhuastive-deps` eslint check ([#45771](https://github.com/WordPress/gutenberg/pull/45771)).
- `ToggleGroupControl`: Update `useUpdateEffect` usages to pass `exhaustive-deps` eslint rule ([#45771](https://github.com/WordPress/gutenberg/pull/45771)).

### Documentation

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,20 @@ import {
usePrevious,
useResizeObserver,
} from '@wordpress/compose';
import { forwardRef, useRef, useState } from '@wordpress/element';
import { forwardRef, useRef, useMemo } from '@wordpress/element';

/**
* Internal dependencies
*/
import { View } from '../../view';
import ToggleGroupControlBackdrop from './toggle-group-control-backdrop';
import ToggleGroupControlContext from '../context';
import { useUpdateEffect } from '../../utils/hooks';
import { useControlledValue } from '../../utils/hooks';
import type { WordPressComponentProps } from '../../ui/context';
import type { ToggleGroupControlMainControlProps } from '../types';
import type {
ToggleGroupControlMainControlProps,
ToggleGroupControlContextProps,
} from '../types';

function UnforwardedToggleGroupControlAsButtonGroup(
{
Expand All @@ -46,39 +49,26 @@ function UnforwardedToggleGroupControlAsButtonGroup(
ToggleGroupControlAsButtonGroup,
'toggle-group-control-as-button-group'
).toString();
const [ selectedValue, setSelectedValue ] = useState( value );
const groupContext = {
baseId,
state: selectedValue,
setState: setSelectedValue,
};
const previousValue = usePrevious( value );

// Propagate groupContext.state change.
useUpdateEffect( () => {
// Avoid calling onChange if groupContext state changed
// from incoming value.
if ( previousValue !== groupContext.state ) {
onChange( groupContext.state );
}
}, [ groupContext.state ] );

// Sync incoming value with groupContext.state.
useUpdateEffect( () => {
if ( value !== groupContext.state ) {
groupContext.setState( value );
}
}, [ value ] );

const [ selectedValue, setSelectedValue ] = useControlledValue( {
defaultValue: previousValue,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we need this line (and probably we don't need previousValue at all anymore)

onChange,
value,
} );
// Expose selectedValue getter/setter via context
const groupContext: ToggleGroupControlContextProps = useMemo(
() => ( {
baseId,
state: selectedValue,
setState: setSelectedValue,
isBlock: ! isAdaptiveWidth,
isDeselectable: true,
size,
} ),
[ baseId, selectedValue, setSelectedValue, isAdaptiveWidth, size ]
);
return (
<ToggleGroupControlContext.Provider
value={ {
...groupContext,
isBlock: ! isAdaptiveWidth,
isDeselectable: true,
size,
} }
>
<ToggleGroupControlContext.Provider value={ groupContext }>
<View
aria-label={ label }
{ ...otherProps }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,14 +61,17 @@ function UnforwardedToggleGroupControlAsRadioGroup(
if ( previousValue !== radio.state ) {
onChange( radio.state );
}
}, [ radio.state ] );
}, [ radio.state, previousValue, onChange ] );

// Sync incoming value with radio.state.
const { state: radioState, setState: setRadioState } = radio;
useUpdateEffect( () => {
if ( value !== radio.state ) {
radio.setState( value );
if ( value !== radioState ) {
setRadioState( value );
}
}, [ value ] );
// setRadioState needs to be listed even if in theory it's supposed to be a
// stable reference — that's an ESLint limitation.
}, [ value, radioState, setRadioState ] );

return (
<ToggleGroupControlContext.Provider
Expand Down