diff --git a/packages/strapi-plugin-upload/admin/src/components/FiltersPicker/FiltersCard/index.js b/packages/strapi-plugin-upload/admin/src/components/FiltersPicker/FiltersCard/index.js index 3ae70c3c4d..e954b6bccf 100644 --- a/packages/strapi-plugin-upload/admin/src/components/FiltersPicker/FiltersCard/index.js +++ b/packages/strapi-plugin-upload/admin/src/components/FiltersPicker/FiltersCard/index.js @@ -1,9 +1,8 @@ -import React, { useReducer, useEffect } from 'react'; +import React, { useReducer, useEffect, useRef } from 'react'; import PropTypes from 'prop-types'; import { FormattedMessage } from 'react-intl'; - -import { useIsMounted } from '@buffetjs/hooks'; import { Select } from '@buffetjs/core'; + import { getFilterType, request } from 'strapi-helper-plugin'; import { getTrad } from '../../../utils'; @@ -15,11 +14,21 @@ import FilterButton from './FilterButton'; import FilterInput from './FilterInput'; const FiltersCard = ({ onChange, filters }) => { - return null; - const isMounted = useIsMounted(); + // Not using the hook from buffet.js because it appears that when the component is closed we the hooks returns false + // Until we make a PR on @buffetjs/hooks I rather use this custom one + const isMounted = useRef(true); const [state, dispatch] = useReducer(reducer, initialState); - const { name, filter, filtersForm, value } = state.toJS(); + useEffect(() => { + if (isMounted.current) { + fetchTimestampNames(); + } + + return () => (isMounted.current = false); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [isMounted]); + + const { name, filter, filtersForm, value } = state.toJS(); const type = filtersForm[name].type; const filtersOptions = getFilterType(type); const options = ['image', 'video', 'file'].filter( @@ -67,22 +76,19 @@ const FiltersCard = ({ onChange, filters }) => { method: 'GET', }); - dispatch({ - type: 'HANDLE_CUSTOM_TIMESTAMPS', - timestamps: result.data.contentType.schema.options.timestamps, - }); + if (isMounted.current) { + dispatch({ + type: 'HANDLE_CUSTOM_TIMESTAMPS', + timestamps: result.data.contentType.schema.options.timestamps, + }); + } } catch (err) { - if (isMounted) { + if (isMounted.current) { strapi.notification.error('notification.error'); } } }; - useEffect(() => { - fetchTimestampNames(); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - return ( diff --git a/packages/strapi-plugin-upload/admin/src/components/FiltersPicker/FiltersCard/reducer.js b/packages/strapi-plugin-upload/admin/src/components/FiltersPicker/FiltersCard/reducer.js index b768705d2d..5805d314f3 100644 --- a/packages/strapi-plugin-upload/admin/src/components/FiltersPicker/FiltersCard/reducer.js +++ b/packages/strapi-plugin-upload/admin/src/components/FiltersPicker/FiltersCard/reducer.js @@ -28,13 +28,25 @@ function reducer(state, action) { return state.update(name, () => value); } case 'HANDLE_CUSTOM_TIMESTAMPS': { - return state.set('name', action.timestamps[0]).updateIn(['filtersForm'], filtersFormMap => - filtersFormMap - .set(action.timestamps[1], filtersFormMap.get('updated_at')) - .set(action.timestamps[0], filtersFormMap.get('created_at')) - .delete('created_at') - .delete('updated_at') - ); + const { + timestamps: [created_at, updated_at], + } = action; + + return state + .update('name', () => created_at) + .updateIn(['filtersForm'], object => { + return object.keySeq().reduce((acc, current) => { + if (current === 'created_at') { + return acc.set(created_at, object.get('created_at')).remove('created_at'); + } + + if (current === 'updated_at') { + return acc.set(updated_at, object.get('updated_at')).remove('updated_at'); + } + + return acc; + }, object); + }); } case 'RESET_FORM': return initialState diff --git a/packages/strapi-plugin-upload/admin/src/components/FiltersPicker/FiltersCard/tests/reducer.test.js b/packages/strapi-plugin-upload/admin/src/components/FiltersPicker/FiltersCard/tests/reducer.test.js index 584801ad80..2a35ce164a 100644 --- a/packages/strapi-plugin-upload/admin/src/components/FiltersPicker/FiltersCard/tests/reducer.test.js +++ b/packages/strapi-plugin-upload/admin/src/components/FiltersPicker/FiltersCard/tests/reducer.test.js @@ -1,3 +1,4 @@ +import { fromJS } from 'immutable'; import reducer, { initialState } from '../reducer'; describe('Upload | components | FiltersCard | reducer', () => { @@ -31,17 +32,68 @@ describe('Upload | components | FiltersCard | reducer', () => { expect(actual).toEqual(expected); }); it('should return the updated filters form with custom timestamps', () => { - const state = initialState; + const state = fromJS({ + name: 'created_at', + filter: '=', + value: 'test', + filtersForm: { + created_at: { + type: 'datetime', + defaultFilter: '=', + defaultValue: 'test1', + }, + updated_at: { + type: 'datetime', + defaultFilter: '=', + defaultValue: 'test2', + }, + size: { + type: 'integer', + defaultFilter: '=', + defaultValue: '0KB', + }, + mime: { + type: 'enum', + defaultFilter: '_contains', + defaultValue: 'image', + }, + }, + }); const action = { type: 'HANDLE_CUSTOM_TIMESTAMPS', timestamps: ['createdAtCustom', 'updatedAtCustom'], }; - const actualFiltersFormKeys = Object.keys(reducer(state, action).toJS().filtersForm); - const expectedFiltersFormKeys = ['createdAtCustom', 'updatedAtCustom', 'size', 'mime']; + const expected = fromJS({ + name: 'createdAtCustom', + filter: '=', + value: 'test', + filtersForm: { + createdAtCustom: { + type: 'datetime', + defaultFilter: '=', + defaultValue: 'test1', + }, + updatedAtCustom: { + type: 'datetime', + defaultFilter: '=', + defaultValue: 'test2', + }, + size: { + type: 'integer', + defaultFilter: '=', + defaultValue: '0KB', + }, + mime: { + type: 'enum', + defaultFilter: '_contains', + defaultValue: 'image', + }, + }, + }); - expect(actualFiltersFormKeys).toEqual(expectedFiltersFormKeys); + expect(reducer(state, action)).toEqual(expected); }); it('should return the initialState on reset', () => { diff --git a/packages/strapi-plugin-upload/admin/src/containers/HomePage/index.js b/packages/strapi-plugin-upload/admin/src/containers/HomePage/index.js index f9b97c0eca..4c36fa5a60 100644 --- a/packages/strapi-plugin-upload/admin/src/containers/HomePage/index.js +++ b/packages/strapi-plugin-upload/admin/src/containers/HomePage/index.js @@ -1,8 +1,8 @@ -import React, { useReducer, useState, useEffect } from 'react'; +import React, { useReducer, useState, useRef, useEffect } from 'react'; import { includes, isEmpty, toString } from 'lodash'; import { useHistory, useLocation } from 'react-router-dom'; import { Header } from '@buffetjs/custom'; -import { useDebounce, useIsMounted } from '@buffetjs/hooks'; +import { useDebounce } from '@buffetjs/hooks'; import { HeaderSearch, PageFooter, @@ -46,7 +46,7 @@ const HomePage = () => { const [searchValue, setSearchValue] = useState(query.get('_q') || ''); const { push } = useHistory(); const { search } = useLocation(); - const isMounted = useIsMounted(); + const isMounted = useRef(true); const { data, dataCount, dataToDelete, isLoading } = reducerState.toJS(); const pluginName = formatMessage({ id: getTrad('plugin.name') }); const paramsKeys = ['_limit', '_start', '_q', '_sort']; @@ -59,12 +59,11 @@ const HomePage = () => { }, [debouncedSearch]); useEffect(() => { - if (isMounted) { - fetchListData(); - } + fetchListData(); + return () => (isMounted.current = false); // eslint-disable-next-line react-hooks/exhaustive-deps - }, [search, isMounted]); + }, [search]); const deleteMedia = async id => { const requestURL = getRequestUrl(`files/${id}`); @@ -74,7 +73,7 @@ const HomePage = () => { method: 'DELETE', }); } catch (err) { - if (isMounted) { + if (isMounted.current) { strapi.notification.error('notification.error'); } } @@ -85,7 +84,7 @@ const HomePage = () => { const [data, count] = await Promise.all([fetchData(), fetchDataCount()]); - if (isMounted) { + if (isMounted.current) { dispatch({ type: 'GET_DATA_SUCCEEDED', data, @@ -105,7 +104,7 @@ const HomePage = () => { return Promise.resolve(data); } catch (err) { - if (isMounted) { + if (isMounted.current) { dispatch({ type: 'GET_DATA_ERROR' }); strapi.notification.error('notification.error'); } @@ -124,7 +123,7 @@ const HomePage = () => { return Promise.resolve(count); } catch (err) { - if (isMounted) { + if (isMounted.current) { dispatch({ type: 'GET_DATA_ERROR' }); strapi.notification.error('notification.error'); } diff --git a/packages/strapi-plugin-upload/admin/src/containers/SettingsPage/index.js b/packages/strapi-plugin-upload/admin/src/containers/SettingsPage/index.js index 34a5b7b589..b538efc6b9 100644 --- a/packages/strapi-plugin-upload/admin/src/containers/SettingsPage/index.js +++ b/packages/strapi-plugin-upload/admin/src/containers/SettingsPage/index.js @@ -1,6 +1,5 @@ import React, { useEffect, useReducer, useRef } from 'react'; import { Header, Inputs } from '@buffetjs/custom'; -import { useIsMounted } from '@buffetjs/hooks'; import { isEqual } from 'lodash'; import { LoadingIndicatorPage, useGlobalContext, request } from 'strapi-helper-plugin'; @@ -16,7 +15,7 @@ const SettingsPage = () => { const { formatMessage } = useGlobalContext(); const [reducerState, dispatch] = useReducer(reducer, initialState, init); const { initialData, isLoading, modifiedData } = reducerState.toJS(); - const isMounted = useIsMounted(); + const isMounted = useRef(true); const getDataRef = useRef(); const abortController = new AbortController(); @@ -25,7 +24,7 @@ const SettingsPage = () => { const { signal } = abortController; const { data } = await request(getRequestUrl('settings', { method: 'GET', signal })); - if (isMounted) { + if (isMounted.current) { dispatch({ type: 'GET_DATA_SUCCEEDED', data, @@ -37,15 +36,14 @@ const SettingsPage = () => { }; useEffect(() => { - if (isMounted) { - getDataRef.current(); - } + getDataRef.current(); return () => { abortController.abort(); + isMounted.current = false; }; // eslint-disable-next-line react-hooks/exhaustive-deps - }, [isMounted]); + }, []); const handleSubmit = async () => { try { @@ -54,7 +52,7 @@ const SettingsPage = () => { body: modifiedData, }); - if (isMounted) { + if (isMounted.current) { dispatch({ type: 'SUBMIT_SUCCEEDED', });