Skip to content

Commit 28370f0

Browse files
committed
rename useMemoCompare to usePreviousIfEqual
1 parent f9fa702 commit 28370f0

File tree

3 files changed

+23
-15
lines changed

3 files changed

+23
-15
lines changed

now.json

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

src/pages/useMemoCompare.md

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

src/pages/usePreviousIfEqual.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: usePreviousIfEqual
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 = usePreviousIfEqual(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 usePreviousIfEqual(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 [usePrevious](https://usehooks.com/usePrevious), in that it gives us the previous value of whatever we pass in, but it only does so if our compare function determines the new value is equal. This is useful if we have an object that's technically a new object on every render, but we'd like the old object reference if we consider the new object to be the same. The compare function can compare nested properties, call object methods, or whatever else you need to do in order to determine equality.
14+
<br/><br/>
15+
Most of the time this hook shouldn't be necessary. Where it really comes in handy is if you want to offer a library to other developers, but don't want to require them to memoize a value before passing it to your library. If that value changes on every render and is also passed to <code>useEffect</code> as a dependency then that effect will fire on every render. If that effect updates state you'll find yourself in an infinite loop 🌀 🙀
16+
<br/><br/>
17+
In many ways this is similar to memoizing with <code>useMemo</code>, except we're not checking dependencies to avoid computation. We just want control over whether we use a previous value or not. Read through the recipe and inline comments below to get a better sense of how this all works. For a more practical example be sure to also check out our [useFirebaseQuery](https://usehooks.com/useFirebaseQuery) hook.

0 commit comments

Comments
 (0)