From 3b3a68698fad0a1154b4d0f620235c84ec26b2b4 Mon Sep 17 00:00:00 2001 From: Jean-Philippe Sirois Date: Fri, 8 Nov 2019 16:58:31 -0800 Subject: [PATCH 1/2] refact: extract location to provider/hook --- src/LocationProvider.tsx | 33 +++++++++++++++++++++++++++++++++ src/hooks/useLocation.tsx | 12 ++++++++++++ src/layouts/Layout/index.tsx | 5 +++-- src/layouts/index.tsx | 18 ++++++++++++------ 4 files changed, 60 insertions(+), 8 deletions(-) create mode 100644 src/LocationProvider.tsx create mode 100644 src/hooks/useLocation.tsx diff --git a/src/LocationProvider.tsx b/src/LocationProvider.tsx new file mode 100644 index 0000000..84816bc --- /dev/null +++ b/src/LocationProvider.tsx @@ -0,0 +1,33 @@ +import { WindowLocation } from "@reach/router" +import React, { FC, PropsWithChildren, useMemo } from "react" + +export interface ILocationContextInterface { + location: WindowLocation | void + isHome: boolean +} + +interface ILocationProviderProps { + location: WindowLocation | void +} + +export const LocationContext = React.createContext( + null +) + +export const LocationProvider: FC< + PropsWithChildren +> = ({ children, location }) => { + const memoizedContextValue = useMemo( + () => ({ + location, + isHome: location ? location.pathname === "/" : false, + }), + [location] + ) + + return ( + + {children} + + ) +} diff --git a/src/hooks/useLocation.tsx b/src/hooks/useLocation.tsx new file mode 100644 index 0000000..730a9f5 --- /dev/null +++ b/src/hooks/useLocation.tsx @@ -0,0 +1,12 @@ +import { useContext } from "react" +import { ILocationContextInterface, LocationContext } from "../LocationProvider" + +export default function useLocation(): ILocationContextInterface { + const locationContext = useContext(LocationContext) + + if (!locationContext) { + throw Error("Need context") + } + + return locationContext +} diff --git a/src/layouts/Layout/index.tsx b/src/layouts/Layout/index.tsx index 339806f..cc9fcf9 100644 --- a/src/layouts/Layout/index.tsx +++ b/src/layouts/Layout/index.tsx @@ -13,12 +13,13 @@ import { Footer } from "../../components/Footer" import { Header } from "../../components/Header" import { WavesBottom } from "../../components/Waves" import { GlobalStyles } from "../../globalStyles" +import useLocation from "../../hooks/useLocation" import { useLockBodyScroll } from "../../hooks/useLockBodyScroll" import { ThemeProvider } from "../../ThemeProvider" import * as SC from "./styles" -export const Layout: FC = ({ children, location }) => { - const isHome = location ? location.pathname === "/" : false +export const Layout: FC = ({ children }) => { + const { isHome } = useLocation() const { locked, unlock } = useLockBodyScroll() useEffect(() => { diff --git a/src/layouts/index.tsx b/src/layouts/index.tsx index 8b4f54b..10dcad1 100644 --- a/src/layouts/index.tsx +++ b/src/layouts/index.tsx @@ -1,4 +1,6 @@ +import { WindowLocation } from "@reach/router" import React, { PropsWithChildren } from "react" +import { LocationProvider } from "../LocationProvider" import { Layout } from "./Layout" import { ResourcesLayout } from "./ResourcesLayout" @@ -10,11 +12,15 @@ export default function BaseLayout({ pageContext: { layout?: string } - location: Location + location: WindowLocation }>) { - if (pageContext.layout === "resources") { - return {children} - } - - return {children} + return ( + + {pageContext.layout === "resources" ? ( + {children} + ) : ( + {children} + )} + + ) } From 3f94a780be864f3ee040e16ae3d6e36816bc5190 Mon Sep 17 00:00:00 2001 From: Jean-Philippe Sirois Date: Fri, 8 Nov 2019 19:22:33 -0800 Subject: [PATCH 2/2] feat(resources): open current path on page load --- src/LocationProvider.tsx | 10 ++++++++++ src/components/ResourcesSidebar/index.tsx | 10 ++++++++++ src/components/ResourcesSidebar/useMatchingPath.tsx | 12 ++++++++++++ 3 files changed, 32 insertions(+) create mode 100644 src/components/ResourcesSidebar/useMatchingPath.tsx diff --git a/src/LocationProvider.tsx b/src/LocationProvider.tsx index 84816bc..4b8fd92 100644 --- a/src/LocationProvider.tsx +++ b/src/LocationProvider.tsx @@ -1,9 +1,14 @@ import { WindowLocation } from "@reach/router" import React, { FC, PropsWithChildren, useMemo } from "react" +function appendSlashToPath(path: string): string { + return path.endsWith("/") ? path : `${path}/` +} + export interface ILocationContextInterface { location: WindowLocation | void isHome: boolean + isMatchingPath: (path: string) => boolean } interface ILocationProviderProps { @@ -21,6 +26,11 @@ export const LocationProvider: FC< () => ({ location, isHome: location ? location.pathname === "/" : false, + isMatchingPath: (path: string) => { + return location + ? appendSlashToPath(location.pathname).startsWith(path) + : false + }, }), [location] ) diff --git a/src/components/ResourcesSidebar/index.tsx b/src/components/ResourcesSidebar/index.tsx index 001451d..76c6953 100644 --- a/src/components/ResourcesSidebar/index.tsx +++ b/src/components/ResourcesSidebar/index.tsx @@ -10,6 +10,7 @@ import { ThemeToggler } from "../ThemeToggler" import useSidebar from "./../../hooks/useSidebar" import * as SC from "./styles" import useBuildTree from "./useBuildTree" +import useMatchingPath from "./useMatchingPath" const ALL_RESOURCES = graphql` query { @@ -49,6 +50,10 @@ function plantTree(item: IFileOrFolder, index?: number, firstLevel?: boolean) { function Folder({ item }: { item: IFolder }) { const [collapsed, setCollapse] = useState(true) + useMatchingPath(item.path, () => { + setCollapse(false) + }) + function toggleCollapse() { setCollapse(prevState => !prevState) } @@ -66,6 +71,11 @@ function Folder({ item }: { item: IFolder }) { const FirstLevelFolder = memo( ({ item, index }: { item: IFolder; index: number }) => { const { current, setCurrent } = useSidebar() + + useMatchingPath(item.path, () => { + setCurrent(index) + }) + const collapsed = current !== index return ( diff --git a/src/components/ResourcesSidebar/useMatchingPath.tsx b/src/components/ResourcesSidebar/useMatchingPath.tsx new file mode 100644 index 0000000..a0db98e --- /dev/null +++ b/src/components/ResourcesSidebar/useMatchingPath.tsx @@ -0,0 +1,12 @@ +import { useEffect } from "react" +import useLocation from "../../hooks/useLocation" + +export default function useMatchingPath(path: string, callback: () => void) { + const { isMatchingPath } = useLocation() + + useEffect(() => { + if (isMatchingPath(path)) { + callback() + } + }, []) +}