From 2298b1d377df19e9d422f73846a1c6fbf584a81f Mon Sep 17 00:00:00 2001 From: rektdeckard Date: Mon, 14 Sep 2020 17:40:09 -0400 Subject: [PATCH 1/5] selectors: use synchronous selectors returning Promise over async --- src/state/selectors.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/state/selectors.ts b/src/state/selectors.ts index f67046c..81aa453 100644 --- a/src/state/selectors.ts +++ b/src/state/selectors.ts @@ -15,7 +15,7 @@ const isQueryMatch = (icon: IconEntry, query: string): boolean => { export const filteredQueryResultsSelector = selector>({ key: "filteredQueryResultsSelector", - get: async ({ get }) => { + get: ({ get }) => { const query = get(searchQueryAtom).trim().toLowerCase(); const style = get(iconStyleAtom); @@ -33,7 +33,7 @@ export const categorizedQueryResultsSelector = selector< Readonly >({ key: "categorizedQueryResultsSelector", - get: async ({ get }) => { + get: ({ get }) => { const filteredResults = get(filteredQueryResultsSelector); return new Promise((resolve) => resolve( @@ -54,7 +54,7 @@ export const singleCategoryQueryResultsSelector = selectorFamily< IconCategory >({ key: "singleCategoryQueryResultsSelector", - get: (category: IconCategory) => async ({ get }) => { + get: (category: IconCategory) => ({ get }) => { const filteredResults = get(filteredQueryResultsSelector); return new Promise((resolve) => resolve( From 78a69c9f3bc4db3ddf61753e6172380580a95954 Mon Sep 17 00:00:00 2001 From: rektdeckard Date: Mon, 14 Sep 2020 17:43:01 -0400 Subject: [PATCH 2/5] IconGrid: extract empty state into Warn component The empty state now renders a Warn component, which can be used to show empty queries, or to show an arbitrary error message. --- src/components/IconGrid/IconGrid.tsx | 28 ++++------------------ src/components/Warn/Warn.tsx | 36 ++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 24 deletions(-) create mode 100644 src/components/Warn/Warn.tsx diff --git a/src/components/IconGrid/IconGrid.tsx b/src/components/IconGrid/IconGrid.tsx index 4deb3d0..f167662 100644 --- a/src/components/IconGrid/IconGrid.tsx +++ b/src/components/IconGrid/IconGrid.tsx @@ -2,19 +2,15 @@ import React, { useRef, useEffect } from "react"; import { useRecoilValue } from "recoil"; import { motion, useAnimation } from "framer-motion"; import { useWindowSize } from "react-use"; -import { IconContext, Warning } from "phosphor-react"; +import { IconContext } from "phosphor-react"; -import { - iconStyleAtom, - iconSizeAtom, - iconColorAtom, - searchQueryAtom, -} from "../../state/atoms"; +import { iconStyleAtom, iconSizeAtom, iconColorAtom } from "../../state/atoms"; import { filteredQueryResultsSelector, isDarkThemeSelector, } from "../../state/selectors"; import GridItem from "./IconGridItem"; +import Warn from "../Warn/Warn"; import "./IconGrid.css"; type IconGridProps = {}; @@ -23,7 +19,6 @@ const IconGrid: React.FC = () => { const weight = useRecoilValue(iconStyleAtom); const size = useRecoilValue(iconSizeAtom); const color = useRecoilValue(iconColorAtom); - const query = useRecoilValue(searchQueryAtom); const isDark = useRecoilValue(isDarkThemeSelector); const { width } = useWindowSize(); @@ -38,22 +33,7 @@ const IconGrid: React.FC = () => { controls.start("visible"); }, [controls, filteredQueryResults]); - if (!filteredQueryResults.length) - return ( -
- - -

- No results for '{query}' -

-
-
- ); + if (!filteredQueryResults.length) return ; return ( diff --git a/src/components/Warn/Warn.tsx b/src/components/Warn/Warn.tsx new file mode 100644 index 0000000..d9fa146 --- /dev/null +++ b/src/components/Warn/Warn.tsx @@ -0,0 +1,36 @@ +import React from "react"; +import { motion } from "framer-motion"; +import { useRecoilValue } from "recoil"; + +import { isDarkThemeSelector } from "../../state/selectors"; +import { searchQueryAtom } from "../../state/atoms"; +import { Warning } from "phosphor-react"; + +interface WarnProps { + message?: string; +} + +const Warn: React.FC = ({ message }) => { + const isDark = useRecoilValue(isDarkThemeSelector); + const query = useRecoilValue(searchQueryAtom); + + return ( +
+ + + {message ?? ( +

+ No results for '{query}' +

+ )} +
+
+ ); +}; + +export default Warn; From 7483c85518188126f86cbeecc37f96761ad4b09b Mon Sep 17 00:00:00 2001 From: rektdeckard Date: Mon, 14 Sep 2020 17:45:01 -0400 Subject: [PATCH 3/5] App: add ErrorBoundary around IconGrid The IconGrid can now fallback to an ErrorBoundary, which renders a generic Warn component with a "Search error" message. --- src/components/App/App.tsx | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/components/App/App.tsx b/src/components/App/App.tsx index 7703aaf..507cd7d 100644 --- a/src/components/App/App.tsx +++ b/src/components/App/App.tsx @@ -5,6 +5,8 @@ import Header from "../Header/Header"; import Toolbar from "../Toolbar/Toolbar"; import IconGrid from "../IconGrid/IconGrid"; import Footer from "../Footer/Footer"; +import ErrorBoundary from "../ErrorBoundary/ErrorBoundary"; +import Warn from "../Warn/Warn"; const App: React.FC = () => { return ( @@ -12,9 +14,11 @@ const App: React.FC = () => {
- Loading...}> - - + }> + Loading...}> + + +