diff --git a/packages/block-editor/src/components/link-control/README.md b/packages/block-editor/src/components/link-control/README.md index 9479863e36f407..edab6b4ad488cd 100644 --- a/packages/block-editor/src/components/link-control/README.md +++ b/packages/block-editor/src/components/link-control/README.md @@ -4,6 +4,26 @@ Renders a link control. A link control is a controlled input which maintains a v It is designed to provide a standardized UI for the creation of a link throughout the Editor, see History section at bottom for further background. +## Best Practices + +### Ensuring unique instances + +It is possible that a given editor may render multiple instances of the `` component. As a result, it is important to ensure each instance is unique across the editor to avoid state "leaking" between components. + +Why would this happen? + +React's reconciliation algorithm means that if you return the same element from a component, it keeps the nodes around as an optimization, even if the props change. This means that if you render two (or more) ``s, it is possible that the `value` from one will appear in the other as you move between them. + +As a result it is recommended that consumers provide a `key` prop to each instance of ``: + +```jsx + +``` + +This will cause React to return the same component/element type but force remount a new instance, thus avoiding the issues described above. + +For more information see: https://github.com/WordPress/gutenberg/pull/34742. + ## Relationship to `` As of Gutenberg 7.4, `` became the default link creation interface within the Block Editor, replacing previous disparate uses of `` and standardizing the UI. diff --git a/packages/block-editor/src/components/link-control/use-internal-value.js b/packages/block-editor/src/components/link-control/use-internal-value.js index ac58c05b10a870..9df557be1460cf 100644 --- a/packages/block-editor/src/components/link-control/use-internal-value.js +++ b/packages/block-editor/src/components/link-control/use-internal-value.js @@ -1,21 +1,25 @@ /** * WordPress dependencies */ -import { useState, useEffect } from '@wordpress/element'; +import { useState } from '@wordpress/element'; + +/** + * External dependencies + */ +import fastDeepEqual from 'fast-deep-equal'; export default function useInternalValue( value ) { const [ internalValue, setInternalValue ] = useState( value || {} ); + const [ previousValue, setPreviousValue ] = useState( value ); // If the value prop changes, update the internal state. - useEffect( () => { - setInternalValue( ( prevValue ) => { - if ( value && value !== prevValue ) { - return value; - } - - return prevValue; - } ); - }, [ value ] ); + // See: + // - https://github.com/WordPress/gutenberg/pull/51387#issuecomment-1722927384. + // - https://react.dev/reference/react/useState#storing-information-from-previous-renders. + if ( ! fastDeepEqual( value, previousValue ) ) { + setPreviousValue( value ); + setInternalValue( value ); + } const setInternalURLInputValue = ( nextValue ) => { setInternalValue( {