Skip to content

Commit 37752ac

Browse files
authored
Add support for Next.js (#122)
* Remove rambda * Adds support for Next.js routing * Add Next.js as an optional peer dependency * Prettier fix * Add typings * Remove comment
1 parent e6cc597 commit 37752ac

File tree

7 files changed

+222
-21
lines changed

7 files changed

+222
-21
lines changed

package.json

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@
88
],
99
"exports": {
1010
".": "./dist/index.js",
11-
"./react-router": "./dist/react-router/index.js"
11+
"./react-router": "./dist/react-router/index.js",
12+
"./next": "./dist/next/index.js"
1213
},
1314
"scripts": {
1415
"test": "jest",
@@ -40,8 +41,7 @@
4041
"@mitodl/open-api-axios": "^2024.9.16",
4142
"axios": "^1.6.7",
4243
"fuse.js": "^7.0.0",
43-
"query-string": "^6.13.1",
44-
"ramda": "^0.27.1"
44+
"query-string": "^6.13.1"
4545
},
4646
"devDependencies": {
4747
"@swc/core": "^1.5.7",
@@ -53,8 +53,8 @@
5353
"@types/jest": "^29.0.1",
5454
"@types/lodash": "^4.14.162",
5555
"@types/lodash.uppercase": "^4.3.9",
56+
"@types/next": "^9.0.0",
5657
"@types/node": "^14.6",
57-
"@types/ramda": "^0.27.27",
5858
"@types/react": "16.9.49",
5959
"@types/react-dom": "16.9.8",
6060
"@typescript-eslint/eslint-plugin": "^7.0.1",
@@ -87,6 +87,7 @@
8787
"peerDependencies": {
8888
"@types/history": "^4.9",
8989
"history": "^4.9 || ^5.0.0",
90+
"next": "^14.2.6",
9091
"react": "^16.13.1",
9192
"react-router": "^6.22.2"
9293
},
@@ -97,6 +98,9 @@
9798
"react-router": {
9899
"optional": true
99100
},
101+
"next": {
102+
"optional": true
103+
},
100104
"@types/history": {
101105
"optional": true
102106
}

src/facet_display/Facet.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import React, { useState } from "react"
2-
import { contains } from "ramda"
32

43
import { SearchFacetItem } from "./SearchFacetItem"
54
import { BucketWithLabel } from "./types"
@@ -58,7 +57,7 @@ function SearchFacet(props: Props) {
5857
<SearchFacetItem
5958
key={`${name}-${bucket.key}`}
6059
bucket={bucket}
61-
isChecked={contains(bucket.key, selected || [])}
60+
isChecked={(selected || []).includes(bucket.key)}
6261
onUpdate={onUpdate}
6362
name={name}
6463
displayKey={bucket.label}

src/facet_display/FilterableFacet.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import React, { useState, useCallback, useMemo } from "react"
2-
import { contains } from "ramda"
32
import Fuse from "fuse.js"
43

54
import { SearchFacetItem } from "./SearchFacetItem"
@@ -106,7 +105,7 @@ function FilterableFacet(props: Props) {
106105
<SearchFacetItem
107106
key={`${name}-${bucket.key}`}
108107
bucket={bucket}
109-
isChecked={contains(bucket.key, selected || [])}
108+
isChecked={(selected || []).includes(bucket.key)}
110109
onUpdate={onUpdate}
111110
name={name}
112111
displayKey={bucket.label}

src/facet_display/SanitizeFacets.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { Facets } from "./types"
22
import { LEVELS, DEPARTMENTS } from "../constants"
3+
34
const reverseObject = (
45
stringObject: Record<string, string>
56
): Record<string, string> => {

src/next/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { default as useSearchParams } from "./useSearchParams"

src/next/useSearchParams.ts

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import { useCallback, useEffect, useMemo, useRef } from "react"
2+
import {
3+
usePathname,
4+
useSearchParams as useNextSearchParams,
5+
useRouter
6+
} from "next/navigation"
7+
8+
type SearchParamsSetterValue =
9+
| URLSearchParams
10+
| ((prevSearchParams: URLSearchParams) => URLSearchParams)
11+
12+
type SetSearchParams = (newSearchParams: SearchParamsSetterValue) => void
13+
14+
const useSearchParams = (): [URLSearchParams, SetSearchParams] => {
15+
const pathname = usePathname()
16+
const router = useRouter()
17+
const search = useNextSearchParams()
18+
19+
/**
20+
* Keep track of whether navigate has been called in the current render cycle
21+
* to avoid adding extra entries in the history stack.
22+
*/
23+
const hasNavigatedRef = useRef(false)
24+
const searchParams = useMemo(() => new URLSearchParams(search), [search])
25+
/**
26+
* Keep track of the current searchParams value so that updater functions can
27+
* use the current value rather than value from previous render.
28+
*/
29+
const searchParamsRef = useRef(searchParams)
30+
31+
useEffect(() => {
32+
hasNavigatedRef.current = false
33+
/**
34+
* Each render, sync the ref with the current state value.
35+
* This is necessary in case search params has changed via some source other
36+
* than this hook (e.g., browser navigation).
37+
*/
38+
searchParamsRef.current = searchParams
39+
})
40+
41+
const setSearchParams: SetSearchParams = useCallback(
42+
nextValue => {
43+
const newParams =
44+
typeof nextValue === "function" ?
45+
nextValue(searchParamsRef.current) :
46+
nextValue
47+
48+
searchParamsRef.current = newParams
49+
50+
if (hasNavigatedRef.current) {
51+
router.replace(`${pathname}?${newParams}`)
52+
} else {
53+
router.push(`${pathname}?${newParams}`)
54+
}
55+
56+
hasNavigatedRef.current = true
57+
},
58+
[pathname, router]
59+
)
60+
return [searchParams, setSearchParams]
61+
}
62+
63+
export default useSearchParams

0 commit comments

Comments
 (0)