mirror of
https://github.com/strapi/strapi.git
synced 2025-09-25 16:29:34 +00:00
Fix cleanup in useeffect
Signed-off-by: soupette <cyril.lpz@gmail.com>
This commit is contained in:
parent
62bb0bbae1
commit
16c7d2123f
@ -1,5 +1,5 @@
|
|||||||
import { memo, useCallback, useEffect, useMemo, useRef, useReducer } from 'react';
|
import { memo, useCallback, useEffect, useMemo, useRef, useReducer } from 'react';
|
||||||
import { useParams, useHistory } from 'react-router-dom';
|
import { useHistory } from 'react-router-dom';
|
||||||
import { get } from 'lodash';
|
import { get } from 'lodash';
|
||||||
import { request, useGlobalContext } from 'strapi-helper-plugin';
|
import { request, useGlobalContext } from 'strapi-helper-plugin';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
@ -15,11 +15,10 @@ import { crudInitialState, crudReducer } from '../../sharedReducers';
|
|||||||
import { getRequestUrl } from './utils';
|
import { getRequestUrl } from './utils';
|
||||||
|
|
||||||
// This container is used to handle the CRUD
|
// This container is used to handle the CRUD
|
||||||
const CollectionTypeFormWrapper = ({ allLayoutData, children, from, slug }) => {
|
const CollectionTypeFormWrapper = ({ allLayoutData, children, from, slug, id, origin }) => {
|
||||||
const { emitEvent } = useGlobalContext();
|
const { emitEvent } = useGlobalContext();
|
||||||
const { push, replace } = useHistory();
|
const { push, replace } = useHistory();
|
||||||
|
|
||||||
const { id, origin } = useParams();
|
|
||||||
const [
|
const [
|
||||||
{ componentsDataStructure, contentTypeDataStructure, data, isLoading, status },
|
{ componentsDataStructure, contentTypeDataStructure, data, isLoading, status },
|
||||||
dispatch,
|
dispatch,
|
||||||
@ -99,8 +98,6 @@ const CollectionTypeFormWrapper = ({ allLayoutData, children, from, slug }) => {
|
|||||||
});
|
});
|
||||||
}, [allLayoutData]);
|
}, [allLayoutData]);
|
||||||
|
|
||||||
const shouldFetch = useRef(true);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const abortController = new AbortController();
|
const abortController = new AbortController();
|
||||||
const { signal } = abortController;
|
const { signal } = abortController;
|
||||||
@ -139,7 +136,7 @@ const CollectionTypeFormWrapper = ({ allLayoutData, children, from, slug }) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (requestURL && shouldFetch.current) {
|
if (requestURL) {
|
||||||
getData(signal);
|
getData(signal);
|
||||||
} else {
|
} else {
|
||||||
dispatch({ type: 'INIT_FORM' });
|
dispatch({ type: 'INIT_FORM' });
|
||||||
@ -147,7 +144,6 @@ const CollectionTypeFormWrapper = ({ allLayoutData, children, from, slug }) => {
|
|||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
abortController.abort();
|
abortController.abort();
|
||||||
shouldFetch.current = requestURL === null;
|
|
||||||
};
|
};
|
||||||
}, [requestURL, push, from, cleanReceivedData, cleanClonedData]);
|
}, [requestURL, push, from, cleanReceivedData, cleanClonedData]);
|
||||||
|
|
||||||
@ -315,6 +311,7 @@ const CollectionTypeFormWrapper = ({ allLayoutData, children, from, slug }) => {
|
|||||||
|
|
||||||
CollectionTypeFormWrapper.defaultProps = {
|
CollectionTypeFormWrapper.defaultProps = {
|
||||||
from: '/',
|
from: '/',
|
||||||
|
origin: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
CollectionTypeFormWrapper.propTypes = {
|
CollectionTypeFormWrapper.propTypes = {
|
||||||
@ -335,6 +332,8 @@ CollectionTypeFormWrapper.propTypes = {
|
|||||||
}).isRequired,
|
}).isRequired,
|
||||||
children: PropTypes.func.isRequired,
|
children: PropTypes.func.isRequired,
|
||||||
from: PropTypes.string,
|
from: PropTypes.string,
|
||||||
|
id: PropTypes.string.isRequired,
|
||||||
|
origin: PropTypes.string,
|
||||||
slug: PropTypes.string.isRequired,
|
slug: PropTypes.string.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
import React, { memo, useMemo } from 'react';
|
import React, { memo, useMemo } from 'react';
|
||||||
import { Switch, Route, useRouteMatch, useParams } from 'react-router-dom';
|
import { Switch, Route } from 'react-router-dom';
|
||||||
|
import { get } from 'lodash';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
import { LoadingIndicatorPage, CheckPagePermissions } from 'strapi-helper-plugin';
|
import { LoadingIndicatorPage, CheckPagePermissions } from 'strapi-helper-plugin';
|
||||||
import pluginPermissions from '../../permissions';
|
import pluginPermissions from '../../permissions';
|
||||||
import { ContentTypeLayoutContext } from '../../contexts';
|
import { ContentTypeLayoutContext } from '../../contexts';
|
||||||
@ -10,9 +12,12 @@ import EditSettingsView from '../EditSettingsView';
|
|||||||
import ListView from '../ListView';
|
import ListView from '../ListView';
|
||||||
import ListSettingsView from '../ListSettingsView';
|
import ListSettingsView from '../ListSettingsView';
|
||||||
|
|
||||||
const CollectionTypeRecursivePath = () => {
|
const CollectionTypeRecursivePath = ({
|
||||||
const { url } = useRouteMatch();
|
match: {
|
||||||
const { slug } = useParams();
|
params: { slug },
|
||||||
|
url,
|
||||||
|
},
|
||||||
|
}) => {
|
||||||
const { isLoading, layout, updateLayout } = useFetchContentTypeLayout(slug);
|
const { isLoading, layout, updateLayout } = useFetchContentTypeLayout(slug);
|
||||||
|
|
||||||
const { rawContentTypeLayout, rawComponentsLayouts } = useMemo(() => {
|
const { rawContentTypeLayout, rawComponentsLayouts } = useMemo(() => {
|
||||||
@ -34,12 +39,37 @@ const CollectionTypeRecursivePath = () => {
|
|||||||
return { rawContentTypeLayout, rawComponentsLayouts };
|
return { rawContentTypeLayout, rawComponentsLayouts };
|
||||||
}, [layout]);
|
}, [layout]);
|
||||||
|
|
||||||
if (isLoading) {
|
const uid = get(layout, ['contentType', 'uid'], null);
|
||||||
|
|
||||||
|
// This statement is needed in order to prevent the CollectionTypeFormWrapper effects clean up phase to be run twice.
|
||||||
|
// What can happen is that when navigating from one entry to another the cleanup phase of the fetch data effect is run twice : once when
|
||||||
|
// unmounting, once when the url changes.
|
||||||
|
// Since it can happen that the layout there's a delay when the layout is being fetched and the url changes adding the uid ! == slug
|
||||||
|
// statement prevent the component from being mounted and unmounted twice.
|
||||||
|
if (uid !== slug || isLoading) {
|
||||||
return <LoadingIndicatorPage />;
|
return <LoadingIndicatorPage />;
|
||||||
}
|
}
|
||||||
|
|
||||||
const renderRoute = (_, Component) => {
|
const renderRoute = (
|
||||||
return <Component slug={slug} layout={layout} />;
|
{
|
||||||
|
location: { state },
|
||||||
|
history: { goBack },
|
||||||
|
match: {
|
||||||
|
params: { id, origin },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Component
|
||||||
|
) => {
|
||||||
|
return (
|
||||||
|
<Component
|
||||||
|
slug={slug}
|
||||||
|
layout={layout}
|
||||||
|
state={state}
|
||||||
|
goBack={goBack}
|
||||||
|
id={id}
|
||||||
|
origin={origin}
|
||||||
|
/>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const routes = [
|
const routes = [
|
||||||
@ -79,4 +109,13 @@ const CollectionTypeRecursivePath = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
CollectionTypeRecursivePath.propTypes = {
|
||||||
|
match: PropTypes.shape({
|
||||||
|
url: PropTypes.string.isRequired,
|
||||||
|
params: PropTypes.shape({
|
||||||
|
slug: PropTypes.string.isRequired,
|
||||||
|
}).isRequired,
|
||||||
|
}).isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
export default memo(CollectionTypeRecursivePath);
|
export default memo(CollectionTypeRecursivePath);
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import React, { memo, useCallback, useMemo } from 'react';
|
import React, { memo, useCallback, useMemo } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { get } from 'lodash';
|
import { get } from 'lodash';
|
||||||
import { useHistory, useLocation } from 'react-router-dom';
|
|
||||||
import {
|
import {
|
||||||
BackHeader,
|
BackHeader,
|
||||||
LiLink,
|
LiLink,
|
||||||
@ -31,10 +30,8 @@ import DeleteLink from './DeleteLink';
|
|||||||
import InformationCard from './InformationCard';
|
import InformationCard from './InformationCard';
|
||||||
|
|
||||||
/* eslint-disable react/no-array-index-key */
|
/* eslint-disable react/no-array-index-key */
|
||||||
const EditView = ({ isSingleType, layout, slug }) => {
|
const EditView = ({ isSingleType, goBack, layout, slug, state, id, origin }) => {
|
||||||
const { goBack } = useHistory();
|
|
||||||
const { currentEnvironment, plugins } = useGlobalContext();
|
const { currentEnvironment, plugins } = useGlobalContext();
|
||||||
const { state } = useLocation();
|
|
||||||
// Permissions
|
// Permissions
|
||||||
const viewPermissions = useMemo(() => generatePermissionsObject(slug), [slug]);
|
const viewPermissions = useMemo(() => generatePermissionsObject(slug), [slug]);
|
||||||
const { allowedActions, isLoading: isLoadingForPermissions } = useUserPermissions(
|
const { allowedActions, isLoading: isLoadingForPermissions } = useUserPermissions(
|
||||||
@ -59,7 +56,9 @@ const EditView = ({ isSingleType, layout, slug }) => {
|
|||||||
: pluginPermissions.collectionTypesConfigurations;
|
: pluginPermissions.collectionTypesConfigurations;
|
||||||
}, [isSingleType]);
|
}, [isSingleType]);
|
||||||
|
|
||||||
const configurationsURL = `/plugins/${pluginId}/collectionType/${slug}/configurations/edit`;
|
const configurationsURL = `/plugins/${pluginId}/${
|
||||||
|
isSingleType ? 'singleType' : 'collectionType'
|
||||||
|
}/${slug}/configurations/edit`;
|
||||||
const currentContentTypeLayoutData = useMemo(() => get(layout, ['contentType'], {}), [layout]);
|
const currentContentTypeLayoutData = useMemo(() => get(layout, ['contentType'], {}), [layout]);
|
||||||
|
|
||||||
const DataManagementWrapper = useMemo(
|
const DataManagementWrapper = useMemo(
|
||||||
@ -91,7 +90,7 @@ const EditView = ({ isSingleType, layout, slug }) => {
|
|||||||
|
|
||||||
// TODO: create a hook to handle/provide the permissions this should be done for the i18n feature
|
// TODO: create a hook to handle/provide the permissions this should be done for the i18n feature
|
||||||
return (
|
return (
|
||||||
<DataManagementWrapper allLayoutData={layout} from={from} slug={slug}>
|
<DataManagementWrapper allLayoutData={layout} from={from} slug={slug} id={id} origin={origin}>
|
||||||
{({
|
{({
|
||||||
componentsDataStructure,
|
componentsDataStructure,
|
||||||
contentTypeDataStructure,
|
contentTypeDataStructure,
|
||||||
@ -264,7 +263,10 @@ const EditView = ({ isSingleType, layout, slug }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
EditView.defaultProps = {
|
EditView.defaultProps = {
|
||||||
|
id: null,
|
||||||
isSingleType: false,
|
isSingleType: false,
|
||||||
|
origin: null,
|
||||||
|
state: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
EditView.propTypes = {
|
EditView.propTypes = {
|
||||||
@ -278,7 +280,11 @@ EditView.propTypes = {
|
|||||||
attributes: PropTypes.object.isRequired,
|
attributes: PropTypes.object.isRequired,
|
||||||
}).isRequired,
|
}).isRequired,
|
||||||
}).isRequired,
|
}).isRequired,
|
||||||
|
id: PropTypes.string,
|
||||||
isSingleType: PropTypes.bool,
|
isSingleType: PropTypes.bool,
|
||||||
|
goBack: PropTypes.func.isRequired,
|
||||||
|
origin: PropTypes.string,
|
||||||
|
state: PropTypes.object,
|
||||||
slug: PropTypes.string.isRequired,
|
slug: PropTypes.string.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,16 +1,40 @@
|
|||||||
import React from 'react';
|
import React, { memo, useMemo } from 'react';
|
||||||
import { Switch, Route, useRouteMatch, useParams } from 'react-router-dom';
|
import { Switch, Route } from 'react-router-dom';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
import { LoadingIndicatorPage, CheckPagePermissions } from 'strapi-helper-plugin';
|
import { LoadingIndicatorPage, CheckPagePermissions } from 'strapi-helper-plugin';
|
||||||
import pluginPermissions from '../../permissions';
|
import pluginPermissions from '../../permissions';
|
||||||
import { ContentTypeLayoutContext } from '../../contexts';
|
import { ContentTypeLayoutContext } from '../../contexts';
|
||||||
import { useFetchContentTypeLayout } from '../../hooks';
|
import { useFetchContentTypeLayout } from '../../hooks';
|
||||||
|
import { formatLayoutToApi } from '../../utils';
|
||||||
import EditView from '../EditView';
|
import EditView from '../EditView';
|
||||||
import EditSettingsView from '../EditSettingsView';
|
import EditSettingsView from '../EditSettingsView';
|
||||||
|
|
||||||
const SingleTypeRecursivePath = props => {
|
const SingleTypeRecursivePath = ({
|
||||||
const { url } = useRouteMatch();
|
match: {
|
||||||
const { slug } = useParams();
|
params: { slug },
|
||||||
const { isLoading, layout } = useFetchContentTypeLayout(slug);
|
url,
|
||||||
|
},
|
||||||
|
}) => {
|
||||||
|
const { isLoading, layout, updateLayout } = useFetchContentTypeLayout(slug);
|
||||||
|
|
||||||
|
const { rawContentTypeLayout, rawComponentsLayouts } = useMemo(() => {
|
||||||
|
let rawComponentsLayouts = {};
|
||||||
|
let rawContentTypeLayout = {};
|
||||||
|
|
||||||
|
if (layout.contentType) {
|
||||||
|
rawContentTypeLayout = formatLayoutToApi(layout.contentType);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (layout.components) {
|
||||||
|
rawComponentsLayouts = Object.keys(layout.components).reduce((acc, current) => {
|
||||||
|
acc[current] = formatLayoutToApi(layout.components[current]);
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
}
|
||||||
|
|
||||||
|
return { rawContentTypeLayout, rawComponentsLayouts };
|
||||||
|
}, [layout]);
|
||||||
|
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
return <LoadingIndicatorPage />;
|
return <LoadingIndicatorPage />;
|
||||||
@ -19,21 +43,36 @@ const SingleTypeRecursivePath = props => {
|
|||||||
return (
|
return (
|
||||||
<ContentTypeLayoutContext.Provider value={layout}>
|
<ContentTypeLayoutContext.Provider value={layout}>
|
||||||
<Switch>
|
<Switch>
|
||||||
|
<Route path={`${url}/configurations/edit`}>
|
||||||
|
<CheckPagePermissions permissions={pluginPermissions.singleTypesConfigurations}>
|
||||||
|
<EditSettingsView
|
||||||
|
components={rawComponentsLayouts}
|
||||||
|
isContentTypeView
|
||||||
|
mainLayout={rawContentTypeLayout}
|
||||||
|
slug={slug}
|
||||||
|
updateLayout={updateLayout}
|
||||||
|
/>
|
||||||
|
</CheckPagePermissions>
|
||||||
|
</Route>
|
||||||
<Route
|
<Route
|
||||||
path={`${url}/ctm-configurations/edit-settings/:type`}
|
path={url}
|
||||||
render={routeProps => (
|
render={({ location: { state }, history: { goBack } }) => {
|
||||||
<CheckPagePermissions permissions={pluginPermissions.singleTypesConfigurations}>
|
return (
|
||||||
<EditSettingsView {...props} {...routeProps} slug={slug} />
|
<EditView layout={layout} slug={slug} isSingleType state={state} goBack={goBack} />
|
||||||
</CheckPagePermissions>
|
);
|
||||||
)}
|
}}
|
||||||
/>
|
|
||||||
<Route
|
|
||||||
path={`${url}`}
|
|
||||||
render={() => <EditView layout={layout} slug={slug} isSingleType />}
|
|
||||||
/>
|
/>
|
||||||
</Switch>
|
</Switch>
|
||||||
</ContentTypeLayoutContext.Provider>
|
</ContentTypeLayoutContext.Provider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default SingleTypeRecursivePath;
|
SingleTypeRecursivePath.propTypes = {
|
||||||
|
match: PropTypes.shape({
|
||||||
|
url: PropTypes.string.isRequired,
|
||||||
|
params: PropTypes.shape({
|
||||||
|
slug: PropTypes.string.isRequired,
|
||||||
|
}).isRequired,
|
||||||
|
}).isRequired,
|
||||||
|
};
|
||||||
|
export default memo(SingleTypeRecursivePath);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user