1313 * See the License for the specific language governing permissions and
1414 * limitations under the License.
1515 */
16- import React , { useCallback , useEffect , useMemo , useState } from 'react' ;
16+ import React , { useCallback , useMemo , useState } from 'react' ;
1717import { JsonObject } from '@backstage/types' ;
1818import { Widget } from '@rjsf/utils' ;
1919import { fetchApiRef , useApi } from '@backstage/core-plugin-api' ;
2020import { JSONSchema7 } from 'json-schema' ;
21+ import { useDebounce } from 'react-use' ;
2122
2223import { useWrapperFormPropsContext } from '@red-hat-developer-hub/backstage-plugin-orchestrator-form-api' ;
2324import { FormContextData } from '../types' ;
@@ -34,6 +35,7 @@ import {
3435 applySelectorString ,
3536} from '../utils/applySelector' ;
3637import { Autocomplete , AutocompleteRenderInputParams } from '@material-ui/lab' ;
38+ import { DEFAULT_DEBOUNCE_LIMIT } from './constants' ;
3739
3840export const ActiveTextInput : Widget <
3941 JsonObject ,
@@ -43,7 +45,6 @@ export const ActiveTextInput: Widget<
4345 const fetchApi = useApi ( fetchApiRef ) ;
4446 const templateUnitEvaluator = useTemplateUnitEvaluator ( ) ;
4547 const formContext = useWrapperFormPropsContext ( ) ;
46- const [ _ , setLoading ] = useState ( true ) ;
4748 const [ error , setError ] = useState < string > ( ) ;
4849 const [ autocompleteOptions , setAutocompleteOptions ] = useState < string [ ] > ( ) ;
4950
@@ -65,6 +66,7 @@ export const ActiveTextInput: Widget<
6566 /* This is safe retype, since proper checking of input value is done in the useRetriggerEvaluate() hook */
6667 uiProps [ 'fetch:retrigger' ] as string [ ] ,
6768 ) ;
69+ const isValueSet = value === undefined ;
6870
6971 const evaluatedFetchUrl = useEvaluateTemplate ( {
7072 template : fetchUrl ,
@@ -86,8 +88,8 @@ export const ActiveTextInput: Widget<
8688 [ onChange ] ,
8789 ) ;
8890
89- useEffect ( ( ) => {
90- const fetchDefaultData = async ( ) => {
91+ useDebounce (
92+ ( ) => {
9193 if (
9294 ! evaluatedFetchUrl ||
9395 ! evaluatedRequestInit ||
@@ -97,70 +99,74 @@ export const ActiveTextInput: Widget<
9799 // Not yet ready to fetch
98100 return ;
99101 }
100- const firstTime = value === undefined ;
101- if ( ! firstTime && ! autocompleteSelector ) {
102+
103+ if ( ! isValueSet && ! autocompleteSelector ) {
102104 // No need to fetch
103105 return ;
104106 }
105107
106- try {
107- setLoading ( true ) ;
108- setError ( undefined ) ;
108+ const fetchDefaultData = async ( ) => {
109+ try {
110+ setError ( undefined ) ;
109111
110- const response = await fetchApi . fetch (
111- evaluatedFetchUrl ,
112- evaluatedRequestInit ,
113- ) ;
114- const data = ( await response . json ( ) ) as JsonObject ;
112+ const response = await fetchApi . fetch (
113+ evaluatedFetchUrl ,
114+ evaluatedRequestInit ,
115+ ) ;
116+ const data = ( await response . json ( ) ) as JsonObject ;
115117
116- // validate received response before updating
117- if ( ! data ) {
118- throw new Error ( 'Empty response received' ) ;
119- }
120- if ( typeof data !== 'object' ) {
121- throw new Error ( 'JSON object expected' ) ;
122- }
118+ // validate received response before updating
119+ if ( ! data ) {
120+ throw new Error ( 'Empty response received' ) ;
121+ }
122+ if ( typeof data !== 'object' ) {
123+ throw new Error ( 'JSON object expected' ) ;
124+ }
123125
124- const selected = await applySelectorString ( data , defaultValueSelector ) ;
125- if ( autocompleteSelector ) {
126- const autocompleteValues = await applySelectorArray (
126+ const selected = await applySelectorString (
127127 data ,
128- autocompleteSelector ,
128+ defaultValueSelector ,
129+ ) ;
130+ if ( autocompleteSelector ) {
131+ const autocompleteValues = await applySelectorArray (
132+ data ,
133+ autocompleteSelector ,
134+ ) ;
135+ setAutocompleteOptions ( autocompleteValues ) ;
136+ }
137+
138+ if ( isValueSet ) {
139+ // loading default so do it only once
140+ handleChange ( selected ) ;
141+ }
142+ } catch ( err ) {
143+ // eslint-disable-next-line no-console
144+ console . error (
145+ 'Error when fetching default ActiveTextInput data' ,
146+ props . id ,
147+ evaluatedFetchUrl ,
148+ err ,
129149 ) ;
130- setAutocompleteOptions ( autocompleteValues ) ;
150+ setError ( `Failed to fetch data for ${ props . id } ActiveTextInput` ) ;
131151 }
152+ } ;
132153
133- if ( firstTime ) {
134- // loading default so do it only once
135- handleChange ( selected ) ;
136- }
137- } catch ( err ) {
138- // eslint-disable-next-line no-console
139- console . error (
140- 'Error when fetching default ActiveTextInput data' ,
141- props . id ,
142- evaluatedFetchUrl ,
143- err ,
144- ) ;
145- setError ( `Failed to fetch data for ${ props . id } ActiveTextInput` ) ;
146- } finally {
147- setLoading ( false ) ;
148- }
149- } ;
150-
151- fetchDefaultData ( ) ;
152- } , [
153- evaluatedFetchUrl ,
154- evaluatedRequestInit ,
155- autocompleteSelector ,
156- defaultValueSelector ,
157- fetchApi ,
158- props . id ,
159- value ,
160- handleChange ,
161- // no need to expand the "retrigger" array here since its identity changes only if an item changes
162- retrigger ,
163- ] ) ;
154+ fetchDefaultData ( ) ;
155+ } ,
156+ DEFAULT_DEBOUNCE_LIMIT ,
157+ [
158+ evaluatedFetchUrl ,
159+ evaluatedRequestInit ,
160+ autocompleteSelector ,
161+ defaultValueSelector ,
162+ fetchApi ,
163+ props . id ,
164+ handleChange ,
165+ isValueSet ,
166+ // no need to expand the "retrigger" array here since its identity changes only if an item changes
167+ retrigger ,
168+ ] ,
169+ ) ;
164170
165171 if ( ! fetchUrl || ! defaultValueSelector ) {
166172 // eslint-disable-next-line no-console
0 commit comments