Skip to content

Commit 44390a9

Browse files
committed
Revert useMemoCompare name change, improve post text
1 parent ca8ccc9 commit 44390a9

File tree

3 files changed

+17
-23
lines changed

3 files changed

+17
-23
lines changed

now.json

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,5 @@
2020
"src": "/api/subscribe",
2121
"dest": "/mailchimp/index.js"
2222
}
23-
],
24-
"redirects": [
25-
{
26-
"source": "/useMemoCompare",
27-
"destination": "/usePreviousCompare"
28-
}
2923
]
3024
}

src/pages/useMemoCompare.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
---
2+
templateKey: post
3+
title: useMemoCompare
4+
date: "2020-04-08"
5+
gist: https://gist.github.com/gragland/ca6806dbb849efa32be8a6919e281d09
6+
links:
7+
- url: https://github.com/facebook/react/issues/14476
8+
name: useEffect custom comparator
9+
description: Related discussion in the React Github repo that has other potential solutions
10+
code: "import React, { useState, useEffect, useRef } from 'react';\r\n\r\n\/\/ Usage\r\nfunction MyComponent({ obj }) {\r\n const [state, setState] = useState();\r\n \r\n \/\/ Use the previous obj value if the \"id\" property hasn't changed\r\n const objFinal = useMemoCompare(obj, (prev, next) => {\r\n return prev && prev.id === next.id;\r\n });\r\n \r\n \/\/ Here we want to fire off an effect if objFinal changes.\r\n \/\/ If we had used obj directly without the above hook and obj was technically a\r\n \/\/ new object on every render then the effect would fire on every render.\r\n \/\/ Worse yet, if our effect triggered a state change it could cause an endless loop\r\n \/\/ where effect runs -> state change causes rerender -> effect runs -> etc ...\r\n useEffect(() => {\r\n \/\/ Call a method on the object and set results to state\r\n return objFinal.someMethod().then((value) => setState(value));\r\n }, [objFinal]);\r\n \r\n \/\/ So why not pass [obj.id] as the dependency array instead?\r\n useEffect(() => {\r\n \/\/ Then eslint-plugin-hooks would rightfully complain that obj is not in the\r\n \/\/ dependency array and we'd have to use eslint-disable-next-line to work around that. \r\n \/\/ It's much cleaner to just get the old object reference with our custom hook.\r\n return obj.someMethod().then((value) => setState(value));\r\n }, [obj.id]);\r\n \r\n return <div> ... <\/div>;\r\n}\r\n \r\n\/\/ Hook\r\nfunction useMemoCompare(next, compare) {\r\n \/\/ Ref for storing previous value\r\n const previousRef = useRef();\r\n const previous = previousRef.current;\r\n \r\n \/\/ Pass previous and next value to compare function\r\n \/\/ to determine whether to consider them equal.\r\n const isEqual = compare(previous, next);\r\n\r\n \/\/ If not equal update previousRef to next value.\r\n \/\/ We only update if not equal so that this hook continues to return\r\n \/\/ the same old value if compare keeps returning true.\r\n useEffect(() => {\r\n if (!isEqual) {\r\n previousRef.current = next;\r\n }\r\n });\r\n \r\n \/\/ Finally, if equal then return the previous value\r\n return isEqual ? previous : next;\r\n}"
11+
---
12+
13+
This hook is similar to [useMemo](https://reactjs.org/docs/hooks-reference.html#usememo), but instead of passing an array of dependencies we pass a custom compare function that receives the previous and new value. The compare function can then compare nested properties, call object methods, or anything else to determine equality. If the compare function returns true then the hook returns the old object reference.
14+
<br/><br/>
15+
It's worth noting that, unlike useMemo, this hook isn't meant to avoid expensive calculations. It needs to be passed a computed value so that it can compare it to the old value. Where this comes in handy is if you want to offer a library to other developers and it would be annoying to force them to memoize an object before passing it to your library. If that object is created in the component body (often the case if it's based on props) then it's going to be a new object on every render. If that object is a <code>useEffect</code> dependency then it's going to cause the effect to fire on every render, which can lead to problems or even an infinite loop. This hook allows you to avoid that scenario by using the old object reference instead of the new one if your custom comparison function deems them equal.
16+
<br/><br/>
17+
Read through the recipe and comments below. For a more practical example be sure to check out our [useFirestoreQuery](https://usehooks.com/useFirestoreQuery) hook.

src/pages/usePreviousIfEqual.md

Lines changed: 0 additions & 17 deletions
This file was deleted.

0 commit comments

Comments
 (0)