From e8b7b9179ed6d7fc409e3d16847f39bc1f1a2565 Mon Sep 17 00:00:00 2001 From: Dan Knauss Date: Thu, 12 Mar 2026 23:49:15 -0600 Subject: [PATCH 1/2] fix: move authors selector init side effects to useEffect --- src/components/AuthorsSelect.tsx | 83 ++++++++++++++++++++++---------- 1 file changed, 58 insertions(+), 25 deletions(-) diff --git a/src/components/AuthorsSelect.tsx b/src/components/AuthorsSelect.tsx index 071d600f..1c216c4d 100644 --- a/src/components/AuthorsSelect.tsx +++ b/src/components/AuthorsSelect.tsx @@ -1,5 +1,5 @@ import { get, isEqual } from 'lodash'; -import React, { ReactElement, useState } from 'react'; +import React, { ReactElement, useEffect, useRef, useState } from 'react'; import { Styles } from 'react-select'; import type { WP_REST_API_Error, @@ -53,30 +53,63 @@ const AuthorsSelect = ( props: AuthorsSelectProps ): ReactElement => { const isDisabled = ! hasAssignAuthorAction; const [ selected, setSelected ] = useState( [] ); - - const preloadedAuthorIDs = preloadedAuthorOptions.authors.map( author => author.value ); - - if ( ! selected.length && isEqual( preloadedAuthorIDs, currentAuthorIDs ) ) { - setSelected( preloadedAuthorOptions.authors ); - } else if ( currentAuthorIDs !== undefined && currentAuthorIDs.length && ! selected.length ) { - - const path = addQueryArgs( - '/authorship/v1/users/', - { - include: currentAuthorIDs, - orderby: 'include', - post_type: postType, - } - ); - - const api: Promise = apiFetch( { path } ); - - api.then( users => { - setSelected( users.map( createOption ) ); - } ).catch( ( error: WP_REST_API_Error ) => { - onError( error.message ); - } ); - } + const hasInitializedSelection = useRef( false ); + + useEffect( () => { + const preloadedAuthorIDs = preloadedAuthorOptions.authors.map( author => author.value ); + + if ( hasInitializedSelection.current || selected.length ) { + return; + } + + if ( isEqual( preloadedAuthorIDs, currentAuthorIDs ) ) { + setSelected( preloadedAuthorOptions.authors ); + hasInitializedSelection.current = true; + return; + } + + if ( currentAuthorIDs !== undefined && currentAuthorIDs.length ) { + hasInitializedSelection.current = true; + + const path = addQueryArgs( + '/authorship/v1/users/', + { + include: currentAuthorIDs, + orderby: 'include', + post_type: postType, + } + ); + + let isCancelled = false; + const api: Promise = apiFetch( { path } ); + + api.then( users => { + if ( isCancelled ) { + return; + } + + setSelected( users.map( createOption ) ); + } ).catch( ( error: WP_REST_API_Error ) => { + if ( isCancelled ) { + return; + } + + onError( error.message ); + } ); + + return () => { + isCancelled = true; + }; + } + + hasInitializedSelection.current = true; + }, [ + currentAuthorIDs, + onError, + postType, + preloadedAuthorOptions.authors, + selected.length, + ] ); /** * Asynchronously loads the options for the control based on the search parameter. From c7a772ed7c1a87ddc725b9e7042e7ef67310de8e Mon Sep 17 00:00:00 2001 From: Dan Knauss Date: Fri, 13 Mar 2026 08:50:28 -0600 Subject: [PATCH 2/2] fix: remove premature initialization guard in AuthorsSelect effect The fallback `hasInitializedSelection.current = true` at the bottom of the useEffect fired when currentAuthorIDs was initially empty, blocking later re-runs from hydrating the selection once real data arrived. The ref is now only set to true inside the two branches that actually perform initialization (preload match and API fetch), so the effect safely re-runs until data is available. Co-Authored-By: Claude Opus 4.6 --- src/components/AuthorsSelect.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/components/AuthorsSelect.tsx b/src/components/AuthorsSelect.tsx index 1c216c4d..4144aef4 100644 --- a/src/components/AuthorsSelect.tsx +++ b/src/components/AuthorsSelect.tsx @@ -101,8 +101,6 @@ const AuthorsSelect = ( props: AuthorsSelectProps ): ReactElement => { isCancelled = true; }; } - - hasInitializedSelection.current = true; }, [ currentAuthorIDs, onError,