Skip to content

Commit b84d62d

Browse files
committed
further notes on shallow comparison in React
1 parent f2ec4e1 commit b84d62d

7 files changed

+168
-13
lines changed

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ There are many fantastic resources for JavaScript interview questions, videos, a
2121
- :link: [www.geeksforgeeks.org/top-algorithms-and-data-structures-for-competitive-programming/](https://www.geeksforgeeks.org/top-algorithms-and-data-structures-for-competitive-programming/)
2222
- :link: [best javascript-algorithms github repo](https://github.com/trekhleb/javascript-algorithms)
2323
- :link: [14-patterns-to-ace-any-coding-interview-question](https://hackernoon.com/14-patterns-to-ace-any-coding-interview-question-c5bb3357f6ed)
24+
- :link: [Grokking the Coding Interview: Patterns for Coding Questions](https://www.educative.io/collection/5668639101419520/5671464854355968)
2425

2526
### Github Repositories with large collections of problems-and-solutions of some popular Interview challenges
2627

Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
### [What is Shallow Comparison from Official dox](https://reactjs.org/docs/shallow-compare.html)
22

3-
- A> When shallow comparing scalar values (numbers, strings) it compares their values. When comparing objects, it does not compare their attributes - only their references are compared (e.g. "do they point to same object?).
3+
- A> When shallow comparing scalar values (numbers, strings) it compares their values. When comparing objects, it does not compare their attributes - only their references are compared (e.g. "do they point to same object?).
44

5-
- B> Shallow comparison is when the properties of the objects being compared is done using "===" or strict equality and will not conduct comparisons deeper into the properties. So if you shallow compare a deep nested object it will just check the reference not the values inside that object.
5+
- B> Shallow comparison is when the properties of the objects being compared is done using "===" or strict equality and will not conduct comparisons deeper into the properties. So if you shallow compare a deep nested object it will just check the reference not the values inside that object.
66

7-
- C> shallowCompare performs a shallow equality check on the current props and nextProps objects as well as the current state and nextState objects.
8-
It does this by iterating on the keys of the objects being compared and returning true (i.e. the component SHOULD GET UPDATED ) when the values of a key in each object are not strictly equal.
7+
- C> shallowCompare performs a shallow equality check on the current props and nextProps objects as well as the current state and nextState objects.
8+
It does this by iterating on the keys of the objects being compared and returning true (i.e. the component SHOULD GET UPDATED ) when the values of a key in each object are not strictly equal.
99

10-
shallowCompare returns true if the shallow comparison for props or state fails and therefore the component should update.
11-
shallowCompare returns false if the shallow comparison for props and state both pass and therefore the component does not need to update.
12-
.
10+
shallowCompare returns true if the shallow comparison for props or state fails and therefore the component should update.
11+
shallowCompare returns false if the shallow comparison for props and state both pass and therefore the component does not need to update.
12+
.
1313

1414
#### useEffect and shallow comparison
1515

@@ -21,7 +21,48 @@ useEffect‘s primary goal is to encompass any side effect you might want to use
2121

2222
Important features useEffect skips running the effect when things don’t change. You don’t actually have to use the dependency values in the effect. You can pass in a prop value as a dependency.
2323

24+
### Now how does shallow comparison works in React
25+
26+
Shallow compare does check for equality. When comparing scalar values (numbers, strings) it compares their values. When comparing objects, it does not compare their's attributes - only their references are compared (e.g. "do they point to same object in memory ?).
27+
28+
Let's consider following shape of user object
29+
30+
```js
31+
user = {
32+
name: "John",
33+
surname: "Doe"
34+
};
35+
```
36+
37+
**Example 1:**
38+
39+
```js
40+
const user = this.state.user;
41+
user.name = "Jane";
42+
43+
console.log(user === this.state.user); // true
44+
```
45+
46+
Notice you changed users name. Even with this change objects are equal. They references are exactly same. Meaning no change and no re-render
47+
48+
**Example 2:**
49+
50+
```js
51+
const user = clone(this.state.user);
52+
console.log(user === this.state.user); // false
53+
```
54+
55+
Now, without any changes to object properties they are completely different. By cloning original object you create new copy, with different reference.
56+
57+
And the clone function might look as this (ES6 syntax)
58+
59+
`const clone = obj => Object.assign({}, ...obj);`
60+
61+
Shallow compare is efficient way to detect changes. It expect you don't mutate data.
62+
63+
`shallowCompare()` function in React actually works like this (just what the official doc says above) - iterating on the keys of the objects being compared and returning true (i.e. saying that the objects are different meaning a re-render is necessary ) when the values of a key in each object are not strictly equal.
64+
2465
### Further Reading
2566

26-
- 1. [https://reactjs.org/docs/shallow-compare.html](https://reactjs.org/docs/shallow-compare.html)
27-
- 2.
67+
- 1. [https://reactjs.org/docs/shallow-compare.html](https://reactjs.org/docs/shallow-compare.html)
68+
- 2. [https://stackoverflow.com/questions/36084515/how-does-shallow-compare-work-in-react](https://stackoverflow.com/questions/36084515/how-does-shallow-compare-work-in-react)
+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
From [React official guide](https://reactjs.org/docs/hooks-reference.html#functional-updates)
2+
3+
Unlike the setState method found in class components, useState does not automatically merge update objects. You can replicate this behavior by combining the function updater form with object spread syntax:
4+
5+
```js
6+
setState(prevState => {
7+
// Object.assign would also work
8+
return { ...prevState, ...updatedValues };
9+
});
10+
```
11+
12+
Another option is useReducer, which is more suited for managing state objects that contain multiple sub-values.
13+
14+
The key point and guidance to update states
15+
16+
Just as with setState in a class component you need to be careful when updating state derived from something that already is in state. State updates using hooks are also batched and hence whenever you want to update state based on previous one its better to use the callback pattern.
17+
18+
If you e.g. update a count twice in a row, it will not work as expected if you don't use the function version of updating the state.
19+
20+
```js
21+
const { useState } = React;
22+
23+
function App() {
24+
const [count, setCount] = useState(0);
25+
26+
// This will be bad, might lead to more bugs because such code often end up inside a closure which has an outdated value of myState.
27+
function brokenIncrement() {
28+
setCount(count + 1);
29+
setCount(count + 1);
30+
}
31+
32+
// The recommended way is to use a function to update the state
33+
function increment() {
34+
setCount(count => count + 1);
35+
setCount(count => count + 1);
36+
}
37+
38+
return (
39+
<div>
40+
<div>{count}</div>
41+
<button onClick={brokenIncrement}>Broken increment</button>
42+
<button onClick={increment}>Increment</button>
43+
</div>
44+
);
45+
}
46+
47+
ReactDOM.render(<App />, document.getElementById("root"));
48+
```
49+
50+
#### Reference
51+
52+
- [https://stackoverflow.com/questions/55342406/updating-and-merging-state-object-using-react-usestate-hook](https://stackoverflow.com/questions/55342406/updating-and-merging-state-object-using-react-usestate-hook)
53+
-

React/Hooks/useEffect-basics-1.md

+8
Original file line numberDiff line numberDiff line change
@@ -97,3 +97,11 @@ useEffect(() => {
9797
console.log("I will run only when valueA changes");
9898
}, [valueA]);
9999
```
100+
101+
**The reason we need to pass in an empty array as the second argument to useEffect** -
102+
103+
In order to register just once we need to pass in an empty array as the second argument to useEffect.
104+
105+
This (passing the empty array) typically is used to control whether or not the useEffect needs to be re-applied. This array is diffed from the original creation of the effect and the new one being passed in. It will diff the array (just like it does the virtual DOM) and decide if it needs to re-apply the effect.
106+
107+
Passing in an empty array tells React to diff, however there is nothing different between each render so the effect will only be run once. Be aware though, if you are calling a function from props, or relying on props inside the effect you will need to pass them into the array to re-apply the effect.

React/Hooks/useEffect-running-callback-after-setState.md renamed to React/Hooks/useEffect-running-callback-after-setState-IMPORTANT.md

+4-1
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ useEffect hooks takes the second parameter as an array of values which React nee
7575
```js
7676
const [loading, setLoading] = useState(false);
7777

78-
...
78+
....
7979

8080
useEffect(() => {
8181
doSomething(); // This is be executed when `loading` state changes
@@ -140,3 +140,6 @@ const getNextPage = () => {
140140
setIsLoading(true);
141141
};
142142
```
143+
144+
So now the question is - in the case of having multiple **setStates** that we want to wait for, we need multiple **useEffect** hooks.
145+
If you want to take different actions when different states change then yes, we need multiple useEffect() hooks, but if you want to do the sme thing on any of those multiple states change, then you could pass multiple arguments like [isLoading, isUpdated, showError]

React/Hooks/useReducer-basics-1.md

+51
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,57 @@ There are a few rules of thumb to follow: If you state management doesn’t need
9696
- Use useState + useReducer + useContext for advanced/medium size applications.
9797
- Use useState/useReducer + Redux for complex/large size applications.
9898

99+
#### Relation with useState
100+
101+
[https://stackoverflow.com/a/55343617/1902852](https://stackoverflow.com/a/55343617/1902852)
102+
103+
Digging thru the source code and the behavior is due to useState calling useReducer
104+
105+
Internally, useState calls useReducer, which returns whatever state a reducer returns.
106+
107+
https://github.com/facebook/react/blob/2b93d686e3/packages/react-reconciler/src/ReactFiberHooks.js#L1230
108+
109+
```js
110+
111+
useState<S>(
112+
initialState: (() => S) | S,
113+
): [S, Dispatch<BasicStateAction<S>>] {
114+
currentHookNameInDev = 'useState';
115+
...
116+
try {
117+
return updateState(initialState);
118+
} finally {
119+
...
120+
}
121+
},
122+
```
123+
124+
where updateState is the internal implementation for useReducer.
125+
126+
```js
127+
function updateState<S>(
128+
initialState: (() => S) | S,
129+
): [S, Dispatch<BasicStateAction<S>>] {
130+
return updateReducer(basicStateReducer, (initialState: any));
131+
}
132+
133+
useReducer<S, I, A>(
134+
reducer: (S, A) => S,
135+
initialArg: I,
136+
init?: I => S,
137+
): [S, Dispatch<A>] {
138+
currentHookNameInDev = 'useReducer';
139+
updateHookTypesDev();
140+
const prevDispatcher = ReactCurrentDispatcher.current;
141+
ReactCurrentDispatcher.current = InvalidNestedHooksDispatcherOnUpdateInDEV;
142+
try {
143+
return updateReducer(reducer, initialArg, init);
144+
} finally {
145+
ReactCurrentDispatcher.current = prevDispatcher;
146+
}
147+
},
148+
```
149+
99150
#### Further Reading
100151

101152
- 1. [https://www.robinwieruch.de/redux-vs-usereducer/](https://www.robinwieruch.de/redux-vs-usereducer/)

React/Hooks/useState.md renamed to React/Hooks/useState-replace-componentWillReceiveProps-getDerivedStateFromProps.md

+1-3
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ const Message = () => {
3434

3535
**The initial value will be assigned only on the initial render (if it’s a function, it will be executed only on the initial render).**
3636

37-
**In subsequent renders (due to a change of state in the component or a parent component), the argument of the useState hook will be ignored and the current value will be retrieved. It is important to keep this in mind because, for example, if you want to update the state based on the new properties the component receives:**
37+
**In subsequent renders (due to a change of state in the component or a parent component), the argument of the useState hook will be ignored and the current value of the state will be retrieved. It is important to keep this in mind because, for example, if you want to update the state based on the new properties the component receives:**
3838

3939
```js
4040
const Message = props => {
@@ -90,8 +90,6 @@ const SomeComponent = props => {
9090

9191
Pere [Hooks Official Docs](https://reactjs.org/docs/hooks-reference.html) - **During the initial render, the returned state (state) is the same as the value passed as the first argument (initialState). During subsequent re-renders (i.e. after invoking a setState (like setCount or whatever) inside useEffect()), the first value returned by useState will always be, whatever is the most recent state after applying updates.**
9292

93-
**useState** takes an initial state as an argument, and returns the current state and an updater function.
94-
9593
The setState it returns is almost the same used by class components—it can accept a callback that gets the current state as an argument, but it doesn't automatically merge top-level object keys.
9694

9795
Each call to useState is paired with a component, with its state persisting across renders. This means that you can call useState multiple times within a single function component to get multiple independent state values. Because the setState returned isn't scoped to a single component, we can define stateful behaviors independent of the component. This enables powerful new ways to abstract stateful logic.

0 commit comments

Comments
 (0)