mirror of
https://github.com/Unleash/unleash.git
synced 2025-01-31 00:16: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:
parent
1fd6f2a60a
commit
49a63173f8
@ -1,7 +1,6 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
||||||
import { useRequiredQueryParam } from 'hooks/useRequiredQueryParam';
|
import { useRequiredQueryParam } from 'hooks/useRequiredQueryParam';
|
||||||
import { useFeature } from 'hooks/api/getters/useFeature/useFeature';
|
|
||||||
import { FeatureStrategyForm } from 'component/feature/FeatureStrategy/FeatureStrategyForm/FeatureStrategyForm';
|
import { FeatureStrategyForm } from 'component/feature/FeatureStrategy/FeatureStrategyForm/FeatureStrategyForm';
|
||||||
import FormTemplate from 'component/common/FormTemplate/FormTemplate';
|
import FormTemplate from 'component/common/FormTemplate/FormTemplate';
|
||||||
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
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 { ISegment } from 'interfaces/segment';
|
||||||
import { useSegmentsApi } from 'hooks/api/actions/useSegmentsApi/useSegmentsApi';
|
import { useSegmentsApi } from 'hooks/api/actions/useSegmentsApi/useSegmentsApi';
|
||||||
import { formatStrategyName } from 'utils/strategyNames';
|
import { formatStrategyName } from 'utils/strategyNames';
|
||||||
|
import { useFeatureImmutable } from 'hooks/api/getters/useFeature/useFeatureImmutable';
|
||||||
|
|
||||||
export const FeatureStrategyCreate = () => {
|
export const FeatureStrategyCreate = () => {
|
||||||
const projectId = useRequiredPathParam('projectId');
|
const projectId = useRequiredPathParam('projectId');
|
||||||
@ -35,12 +35,16 @@ export const FeatureStrategyCreate = () => {
|
|||||||
|
|
||||||
const { addStrategyToFeature, loading } = useFeatureStrategyApi();
|
const { addStrategyToFeature, loading } = useFeatureStrategyApi();
|
||||||
const { setStrategySegments } = useSegmentsApi();
|
const { setStrategySegments } = useSegmentsApi();
|
||||||
const { feature, refetchFeature } = useFeature(projectId, featureId);
|
|
||||||
const { setToastData, setToastApiError } = useToast();
|
const { setToastData, setToastApiError } = useToast();
|
||||||
const { uiConfig } = useUiConfig();
|
const { uiConfig } = useUiConfig();
|
||||||
const { unleashUrl } = uiConfig;
|
const { unleashUrl } = uiConfig;
|
||||||
const { push } = useHistory();
|
const { push } = useHistory();
|
||||||
|
|
||||||
|
const { feature, refetchFeature } = useFeatureImmutable(
|
||||||
|
projectId,
|
||||||
|
featureId
|
||||||
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Fill in the default values once the strategies have been fetched.
|
// Fill in the default values once the strategies have been fetched.
|
||||||
setStrategy(getStrategyObject(strategies, strategyName, featureId));
|
setStrategy(getStrategyObject(strategies, strategyName, featureId));
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { useFeature } from 'hooks/api/getters/useFeature/useFeature';
|
|
||||||
import { FeatureStrategyForm } from 'component/feature/FeatureStrategy/FeatureStrategyForm/FeatureStrategyForm';
|
import { FeatureStrategyForm } from 'component/feature/FeatureStrategy/FeatureStrategyForm/FeatureStrategyForm';
|
||||||
import FormTemplate from 'component/common/FormTemplate/FormTemplate';
|
import FormTemplate from 'component/common/FormTemplate/FormTemplate';
|
||||||
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
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 { useSegmentsApi } from 'hooks/api/actions/useSegmentsApi/useSegmentsApi';
|
||||||
import { useSegments } from 'hooks/api/getters/useSegments/useSegments';
|
import { useSegments } from 'hooks/api/getters/useSegments/useSegments';
|
||||||
import { formatStrategyName } from 'utils/strategyNames';
|
import { formatStrategyName } from 'utils/strategyNames';
|
||||||
|
import { useFeatureImmutable } from 'hooks/api/getters/useFeature/useFeatureImmutable';
|
||||||
|
|
||||||
export const FeatureStrategyEdit = () => {
|
export const FeatureStrategyEdit = () => {
|
||||||
const projectId = useRequiredPathParam('projectId');
|
const projectId = useRequiredPathParam('projectId');
|
||||||
@ -25,13 +25,17 @@ export const FeatureStrategyEdit = () => {
|
|||||||
const [strategy, setStrategy] = useState<Partial<IFeatureStrategy>>({});
|
const [strategy, setStrategy] = useState<Partial<IFeatureStrategy>>({});
|
||||||
const [segments, setSegments] = useState<ISegment[]>([]);
|
const [segments, setSegments] = useState<ISegment[]>([]);
|
||||||
const { updateStrategyOnFeature, loading } = useFeatureStrategyApi();
|
const { updateStrategyOnFeature, loading } = useFeatureStrategyApi();
|
||||||
const { feature, refetchFeature } = useFeature(projectId, featureId);
|
|
||||||
const { setStrategySegments } = useSegmentsApi();
|
const { setStrategySegments } = useSegmentsApi();
|
||||||
const { setToastData, setToastApiError } = useToast();
|
const { setToastData, setToastApiError } = useToast();
|
||||||
const { uiConfig } = useUiConfig();
|
const { uiConfig } = useUiConfig();
|
||||||
const { unleashUrl } = uiConfig;
|
const { unleashUrl } = uiConfig;
|
||||||
const { push } = useHistory();
|
const { push } = useHistory();
|
||||||
|
|
||||||
|
const { feature, refetchFeature } = useFeatureImmutable(
|
||||||
|
projectId,
|
||||||
|
featureId
|
||||||
|
);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
segments: savedStrategySegments,
|
segments: savedStrategySegments,
|
||||||
refetchSegments: refetchSavedStrategySegments,
|
refetchSegments: refetchSavedStrategySegments,
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import useSWR, { mutate, SWRConfiguration } from 'swr';
|
import useSWR, { SWRConfiguration } from 'swr';
|
||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
import { emptyFeature } from './emptyFeature';
|
import { emptyFeature } from './emptyFeature';
|
||||||
import handleErrorResponses from '../httpErrorResponseHandler';
|
import handleErrorResponses from '../httpErrorResponseHandler';
|
||||||
import { formatApiPath } from 'utils/formatPath';
|
import { formatApiPath } from 'utils/formatPath';
|
||||||
import { IFeatureToggle } from 'interfaces/featureToggle';
|
import { IFeatureToggle } from 'interfaces/featureToggle';
|
||||||
|
|
||||||
interface IUseFeatureOutput {
|
export interface IUseFeatureOutput {
|
||||||
feature: IFeatureToggle;
|
feature: IFeatureToggle;
|
||||||
refetchFeature: () => void;
|
refetchFeature: () => void;
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
@ -13,7 +13,7 @@ interface IUseFeatureOutput {
|
|||||||
error?: Error;
|
error?: Error;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IFeatureResponse {
|
export interface IFeatureResponse {
|
||||||
status: number;
|
status: number;
|
||||||
body?: IFeatureToggle;
|
body?: IFeatureToggle;
|
||||||
}
|
}
|
||||||
@ -23,19 +23,17 @@ export const useFeature = (
|
|||||||
featureId: string,
|
featureId: string,
|
||||||
options?: SWRConfiguration
|
options?: SWRConfiguration
|
||||||
): IUseFeatureOutput => {
|
): IUseFeatureOutput => {
|
||||||
const path = formatApiPath(
|
const path = formatFeatureApiPath(projectId, featureId);
|
||||||
`api/admin/projects/${projectId}/features/${featureId}`
|
|
||||||
);
|
|
||||||
|
|
||||||
const { data, error } = useSWR<IFeatureResponse>(
|
const { data, error, mutate } = useSWR<IFeatureResponse>(
|
||||||
path,
|
['useFeature', path],
|
||||||
() => fetcher(path),
|
() => featureFetcher(path),
|
||||||
options
|
options
|
||||||
);
|
);
|
||||||
|
|
||||||
const refetchFeature = useCallback(() => {
|
const refetchFeature = useCallback(() => {
|
||||||
mutate(path).catch(console.warn);
|
mutate().catch(console.warn);
|
||||||
}, [path]);
|
}, [mutate]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
feature: data?.body || emptyFeature,
|
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);
|
const res = await fetch(path);
|
||||||
|
|
||||||
if (res.status === 404) {
|
if (res.status === 404) {
|
||||||
@ -62,3 +62,12 @@ const fetcher = async (path: string): Promise<IFeatureResponse> => {
|
|||||||
body: await res.json(),
|
body: await res.json(),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const formatFeatureApiPath = (
|
||||||
|
projectId: string,
|
||||||
|
featureId: string
|
||||||
|
): string => {
|
||||||
|
return formatApiPath(
|
||||||
|
`api/admin/projects/${projectId}/features/${featureId}`
|
||||||
|
);
|
||||||
|
};
|
||||||
|
@ -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,
|
||||||
|
};
|
||||||
|
};
|
@ -1,4 +1,4 @@
|
|||||||
import useSWR, { mutate, SWRConfiguration } from 'swr';
|
import useSWR, { SWRConfiguration } from 'swr';
|
||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
import { formatApiPath } from 'utils/formatPath';
|
import { formatApiPath } from 'utils/formatPath';
|
||||||
import handleErrorResponses from '../httpErrorResponseHandler';
|
import handleErrorResponses from '../httpErrorResponseHandler';
|
||||||
@ -17,15 +17,15 @@ export const useFeatureEvents = (
|
|||||||
featureName: string,
|
featureName: string,
|
||||||
options?: SWRConfiguration
|
options?: SWRConfiguration
|
||||||
): IUseEventsOutput => {
|
): IUseEventsOutput => {
|
||||||
const { data, error } = useSWR<{ events: IEvent[] }>(
|
const { data, error, mutate } = useSWR<{ events: IEvent[] }>(
|
||||||
[PATH, featureName],
|
[PATH, featureName],
|
||||||
() => fetchFeatureEvents(featureName),
|
() => fetchFeatureEvents(featureName),
|
||||||
options
|
options
|
||||||
);
|
);
|
||||||
|
|
||||||
const refetchEvents = useCallback(() => {
|
const refetchEvents = useCallback(() => {
|
||||||
mutate(PATH).catch(console.warn);
|
mutate().catch(console.warn);
|
||||||
}, []);
|
}, [mutate]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
events: data?.events || [],
|
events: data?.events || [],
|
||||||
|
Loading…
Reference in New Issue
Block a user