1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-01-25 00:07:47 +01:00

fix: avoid constraint accordion close on focus (#907)

* fix: avoid constraint accordion close on focus

* refactor: fix mutate cache key mismatch
This commit is contained in:
olav 2022-04-26 09:54:16 +02:00 committed by GitHub
parent 1fd6f2a60a
commit 49a63173f8
5 changed files with 73 additions and 20 deletions

View File

@ -1,7 +1,6 @@
import React, { useEffect, useState } from 'react';
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
import { useRequiredQueryParam } from 'hooks/useRequiredQueryParam';
import { useFeature } from 'hooks/api/getters/useFeature/useFeature';
import { FeatureStrategyForm } from 'component/feature/FeatureStrategy/FeatureStrategyForm/FeatureStrategyForm';
import FormTemplate from 'component/common/FormTemplate/FormTemplate';
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
@ -23,6 +22,7 @@ import { CREATE_FEATURE_STRATEGY } from 'component/providers/AccessProvider/perm
import { ISegment } from 'interfaces/segment';
import { useSegmentsApi } from 'hooks/api/actions/useSegmentsApi/useSegmentsApi';
import { formatStrategyName } from 'utils/strategyNames';
import { useFeatureImmutable } from 'hooks/api/getters/useFeature/useFeatureImmutable';
export const FeatureStrategyCreate = () => {
const projectId = useRequiredPathParam('projectId');
@ -35,12 +35,16 @@ export const FeatureStrategyCreate = () => {
const { addStrategyToFeature, loading } = useFeatureStrategyApi();
const { setStrategySegments } = useSegmentsApi();
const { feature, refetchFeature } = useFeature(projectId, featureId);
const { setToastData, setToastApiError } = useToast();
const { uiConfig } = useUiConfig();
const { unleashUrl } = uiConfig;
const { push } = useHistory();
const { feature, refetchFeature } = useFeatureImmutable(
projectId,
featureId
);
useEffect(() => {
// Fill in the default values once the strategies have been fetched.
setStrategy(getStrategyObject(strategies, strategyName, featureId));

View File

@ -1,5 +1,4 @@
import React, { useEffect, useState } from 'react';
import { useFeature } from 'hooks/api/getters/useFeature/useFeature';
import { FeatureStrategyForm } from 'component/feature/FeatureStrategy/FeatureStrategyForm/FeatureStrategyForm';
import FormTemplate from 'component/common/FormTemplate/FormTemplate';
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
@ -15,6 +14,7 @@ import { ISegment } from 'interfaces/segment';
import { useSegmentsApi } from 'hooks/api/actions/useSegmentsApi/useSegmentsApi';
import { useSegments } from 'hooks/api/getters/useSegments/useSegments';
import { formatStrategyName } from 'utils/strategyNames';
import { useFeatureImmutable } from 'hooks/api/getters/useFeature/useFeatureImmutable';
export const FeatureStrategyEdit = () => {
const projectId = useRequiredPathParam('projectId');
@ -25,13 +25,17 @@ export const FeatureStrategyEdit = () => {
const [strategy, setStrategy] = useState<Partial<IFeatureStrategy>>({});
const [segments, setSegments] = useState<ISegment[]>([]);
const { updateStrategyOnFeature, loading } = useFeatureStrategyApi();
const { feature, refetchFeature } = useFeature(projectId, featureId);
const { setStrategySegments } = useSegmentsApi();
const { setToastData, setToastApiError } = useToast();
const { uiConfig } = useUiConfig();
const { unleashUrl } = uiConfig;
const { push } = useHistory();
const { feature, refetchFeature } = useFeatureImmutable(
projectId,
featureId
);
const {
segments: savedStrategySegments,
refetchSegments: refetchSavedStrategySegments,

View File

@ -1,11 +1,11 @@
import useSWR, { mutate, SWRConfiguration } from 'swr';
import useSWR, { SWRConfiguration } from 'swr';
import { useCallback } from 'react';
import { emptyFeature } from './emptyFeature';
import handleErrorResponses from '../httpErrorResponseHandler';
import { formatApiPath } from 'utils/formatPath';
import { IFeatureToggle } from 'interfaces/featureToggle';
interface IUseFeatureOutput {
export interface IUseFeatureOutput {
feature: IFeatureToggle;
refetchFeature: () => void;
loading: boolean;
@ -13,7 +13,7 @@ interface IUseFeatureOutput {
error?: Error;
}
interface IFeatureResponse {
export interface IFeatureResponse {
status: number;
body?: IFeatureToggle;
}
@ -23,19 +23,17 @@ export const useFeature = (
featureId: string,
options?: SWRConfiguration
): IUseFeatureOutput => {
const path = formatApiPath(
`api/admin/projects/${projectId}/features/${featureId}`
);
const path = formatFeatureApiPath(projectId, featureId);
const { data, error } = useSWR<IFeatureResponse>(
path,
() => fetcher(path),
const { data, error, mutate } = useSWR<IFeatureResponse>(
['useFeature', path],
() => featureFetcher(path),
options
);
const refetchFeature = useCallback(() => {
mutate(path).catch(console.warn);
}, [path]);
mutate().catch(console.warn);
}, [mutate]);
return {
feature: data?.body || emptyFeature,
@ -46,7 +44,9 @@ export const useFeature = (
};
};
const fetcher = async (path: string): Promise<IFeatureResponse> => {
export const featureFetcher = async (
path: string
): Promise<IFeatureResponse> => {
const res = await fetch(path);
if (res.status === 404) {
@ -62,3 +62,12 @@ const fetcher = async (path: string): Promise<IFeatureResponse> => {
body: await res.json(),
};
};
export const formatFeatureApiPath = (
projectId: string,
featureId: string
): string => {
return formatApiPath(
`api/admin/projects/${projectId}/features/${featureId}`
);
};

View File

@ -0,0 +1,36 @@
import useSWRImmutable from 'swr/immutable';
import { useCallback } from 'react';
import { emptyFeature } from './emptyFeature';
import {
IUseFeatureOutput,
IFeatureResponse,
featureFetcher,
formatFeatureApiPath,
} from 'hooks/api/getters/useFeature/useFeature';
// useFeatureImmutable is like useFeature, except it won't refetch data on
// focus/reconnect/remount. Useful for <form>s that need a stable copy of
// the data. In particular, the lastSeenAt field may often change.
export const useFeatureImmutable = (
projectId: string,
featureId: string
): IUseFeatureOutput => {
const path = formatFeatureApiPath(projectId, featureId);
const { data, error, mutate } = useSWRImmutable<IFeatureResponse>(
['useFeatureImmutable', path],
() => featureFetcher(path)
);
const refetchFeature = useCallback(() => {
mutate().catch(console.warn);
}, [mutate]);
return {
feature: data?.body || emptyFeature,
refetchFeature,
loading: !error && !data,
status: data?.status,
error,
};
};

View File

@ -1,4 +1,4 @@
import useSWR, { mutate, SWRConfiguration } from 'swr';
import useSWR, { SWRConfiguration } from 'swr';
import { useCallback } from 'react';
import { formatApiPath } from 'utils/formatPath';
import handleErrorResponses from '../httpErrorResponseHandler';
@ -17,15 +17,15 @@ export const useFeatureEvents = (
featureName: string,
options?: SWRConfiguration
): IUseEventsOutput => {
const { data, error } = useSWR<{ events: IEvent[] }>(
const { data, error, mutate } = useSWR<{ events: IEvent[] }>(
[PATH, featureName],
() => fetchFeatureEvents(featureName),
options
);
const refetchEvents = useCallback(() => {
mutate(PATH).catch(console.warn);
}, []);
mutate().catch(console.warn);
}, [mutate]);
return {
events: data?.events || [],