Skip to content

Commit 4258a9e

Browse files
committed
useEffect dependency array
1 parent f21656c commit 4258a9e

8 files changed

+99
-3
lines changed

React/Hooks/convert-ClassBasedForm-to-HooksBasedForm.md

+2
Original file line numberDiff line numberDiff line change
@@ -80,12 +80,14 @@ const FunctionBasedForm = () => {
8080
const [email, setEmail] = useState("");
8181
const [password, setPassword] = useState("");
8282

83+
// Note to declare function inside the function-based-component I have to user the keyword 'const'
8384
const handleSubmit = event => {
8485
event.preventDefault();
8586
console.log(email);
8687
console.log(password);
8788
};
8889

90+
// Note also NO 'this' keyword is needed to invoke the handleSubmit() function in the Form
8991
return (
9092
<Form onSubmit={handleSubmit}>
9193
<h1>Function Based Form</h1>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
https://reactjs.org/docs/hooks-faq.html#how-do-lifecycle-methods-correspond-to-hooks
2+
3+
constructor: Function components don’t need a constructor. You can initialize the state in the useState call. If computing the initial state is expensive, you can pass a function to useState.
4+
5+
**getDerivedStateFromProps**: Schedule an update while rendering instead.
6+
7+
**shouldComponentUpdate**: See React.memo below.
8+
9+
**render**: This is the function component body itself.
10+
11+
**componentDidMount**, **componentDidUpdate**, **componentWillUnmount**: The useEffect Hook can express all combinations of these (including less common cases).
12+
13+
**componentDidCatch** and **getDerivedStateFromError**: There are no Hook equivalents for these methods yet, but they will be added soon.

React/Hooks/useEffect-basics-1.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ The state and state update function come from the state hook called useState
8080

8181
**Does `useEffect` run after every render?** Yes! By default, it runs both after the first render _and_ after every update. (We separately talk about below [how to customize this](#tip-optimizing-performance-by-skipping-effects). Instead of thinking in terms of "mounting" and "updating", you might find it easier to think that effects happen "after render". React guarantees the DOM has been updated by the time it runs the effects.
8282

83-
#### Great explanation of the second array argument to useEffect() - so to control when useEffect() will run
83+
#### Great explanation of the second array (called the dependencies array) argument to useEffect() - so to control when useEffect() will run
8484

8585
[https://medium.com/javascript-in-plain-english/state-management-with-react-hooks-no-redux-or-context-api-8b3035ceecf8](https://medium.com/javascript-in-plain-english/state-management-with-react-hooks-no-redux-or-context-api-8b3035ceecf8)
8686

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
#### The problem around dependency array
2+
3+
I trying to wrap my head around the new hooks api of react. Specifically, I'm trying to construct the classic use case that once was the following:
4+
5+
```js
6+
componentDidUpdate(prevProps) {
7+
if (prevProps.foo !== this.props.foo) {
8+
// animate dom elements here...
9+
this.animateSomething(this.ref, this.props.onAnimationComplete);
10+
}
11+
}
12+
```
13+
14+
Now, I tried to build the same with a function component and useEffect, but can't figure out how to do it. This is what I tried:
15+
16+
```js
17+
useEffect(() => {
18+
animateSomething(ref, props.onAnimationComplete);
19+
}, [props.foo]);
20+
```
21+
22+
This way, the effect is only called when props.foo changes. And that does work – BUT! It appears to be an anti-pattern since the eslint-plugin-react-hooks marks this as an error. All dependencies that are used inside the effect should be declared in the dependencies array. So that means I would have to do the following:
23+
24+
```js
25+
useEffect(() => {
26+
animateSomething(ref, props.onAnimationComplete);
27+
}, [props.foo, ref, props.onAnimationComplete]);
28+
```
29+
30+
That does not lead to the linting error BUT it totally defeats the purpose of only calling the effect when props.foo changes. I don't WANT it to be called when the other props or the ref change.
31+
32+
#### The Solution
33+
34+
const previousFooRef = useRef(props.foo);
35+
36+
```js
37+
useEffect(() => {
38+
if (previousFooRef.current !== props.foo) {
39+
animateSomething(ref, props.onAnimationComplete);
40+
previousFooRef.current = props.foo;
41+
}
42+
}, [props.foo, props.onAnimationComplete]);
43+
```
44+
45+
You can't avoid the complexity of having a condition inside the effect, because without it you will run your animation on mount rather than just when props.foo changes. The condition also allows you to avoid animating when things other than props.foo change.
46+
47+
By including props.onAnimationComplete in the dependencies array, you avoid disabling the lint rule which helps ensure that you don’t introduce future bugs related to missing dependencies.
48+
49+
### Further Reading
50+
51+
- 1. [https://stackoverflow.com/questions/55228102/react-hook-useeffect-dependency-array](https://stackoverflow.com/questions/55228102/react-hook-useeffect-dependency-array)
52+
- 2. [https://codesandbox.io/s/oll0y54436?fontsize=14](https://codesandbox.io/s/oll0y54436?fontsize=14)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
The Issue / Problem - Effect re-run endlessly when using an array as a second argument for useEffect which is not the case when I use the array.length instead
2+
3+
CodeSandbox : [codesandbox.io/s/nrwq08p9zj](codesandbox.io/s/nrwq08p9zj) (you can see that the console keeps logging into console)
4+
5+
Guidance on how to resolve this - This is the expected behaviour. the default behaviour of useEffect's second argument is to do a shallow equal check on what's passed in; since you're passing a new array (since getArray returns a new array instance each time) as the first element of the array (ie, it's a nested array), comparing on it will miss the cache and run the effect again. You can work around this by either making sure you pass the same instance of the array, or by passing values that can be compared shallowly in the array. eg -
6+
7+
```js
8+
useEffect(() => {
9+
/* ... */
10+
}, [someHashingFunction(array)]);
11+
// we could use a hashing function (like murmur3 or md5) to generate a value to compare against
12+
```
13+
14+
This also explains why it doesn't rerender when you use array.length, since it doesn't change between renders; however that is buggy, because it also means your component won't rerender when the array contents change, but with the same length. Don't do this.
15+
16+
#### Further Reading
17+
18+
- 1. [https://github.com/facebook/react/issues/14324](https://github.com/facebook/react/issues/14324)
19+
- 2.

React/Hooks/useEffect-replace-componentWillUnmount.md

+5-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,11 @@ useEffect(() => {
1313
}, []);
1414
```
1515

16-
**IMPORTANT - When you return a function in the callback passed to useEffect, the returned function will be called before the component is removed from the UI. So to clean up the side effects you must return a function.**
16+
**IMPORTANT - When you return a function in the callback passed to useEffect, the returned function will be called before the component is removed from the UI. In other words, Whatever function we return from the useEffect will be treated as componentWillUnmount and will run either when the useEffect runs again or when the component is about to leave the UI.**
17+
**So to clean up the side effects you must return a function.**
18+
19+
**we can also use as many useEffect we want to add. That means you can subscribe to an event and unsubscribe from it inside one useEffect and hit APIs in another useEffect**
20+
[Source](https://medium.com/recraftrelic/usestate-and-useeffect-explained-cdb5dc252baf)
1721

1822
As we discussed previously, we need to pass an empty list as the second argument for useEffect so that the callback will only be called once. This apply to the returned function too.
1923

React/Hooks/useReducers-basics-1.md

+7-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,13 @@ Given the return value from **useReducer** is in the form object, I can do de-st
66

77
`const [state, dispatch] = useReducer(reducer, initialState)`
88

9-
By the [Official doc on the above array-destructuring syntax](https://reactjs.org/docs/hooks-state.html#tip-what-do-square-brackets-mean)
9+
**reducer** is a function that takes a state and action and returns a newState. So the above signature of useReducer() effectively becomes the below
10+
11+
`const [state, dispatch] = useReducer((state, action) => newState)`
12+
13+
The newState returned from the reducer is then consumed by the component via the state variable.
14+
15+
[For the destructuring syntax by the](https://reactjs.org/docs/hooks-state.html#tip-what-do-square-brackets-mean)
1016

1117
It means that we’re making two new variables **state** and **dispatch**, where **state** is set to the first value returned by **useReducer**, and **dispatch** is the second. It is equivalent to this code:
1218

React/Hooks/useRef-basics.md

Whitespace-only changes.

0 commit comments

Comments
 (0)