React internal update mechanism?
<h1 class="name"> My name is Kyrie</h1>
<h1 class="name"My name is IRVING</h1>
Copy the code
React also has a Diff algorithm like Vue, which only updates each render node and content different from the last one, so in the example above, React only needs to do this:
h1.innerText = Kyrie -> h1.innerText = IRVING
UseEffect?
function Demo() {
const initCount = 0
const [count, setCount] = useState(initCount)
// When we pass count as a dependency, the count change is reexecuted
useEffect(() => {
console.log(The value of 'count 'is:', count)
}, [count])
return (
<div>
<h2>{count}</h2>
<button onClick={() => setCount(count + 1)}>count++</button>
</div>
)
}
Copy the code
Therefore, the useEffect update mechanism relies on the dependencies we pass in. All the state values used in useEffect must be declared in the dependency, allowing React to update dependencies internally.
As a result, when dependency states become more numerous, it is inevitable that we will be concerned about performance.
UseEffect The correct way to pass dependencies?
Here’s a requirement: Write a function that automatically increases count+1 every second
function Demo() {
const initCount = 0
const [count, setCount] = useState(initCount)
useEffect(() => {
// Set the timer to execute setCount once per second
const timer = setInterval(() => {
setCount(count + 1)
}, 1000).
return() = > {
clearInterval(timer)
}
}, [])
return (
<div>
<h2>{count}</h2>
<button onClick={() => setCount(count + 1)}>count++</button>
</div>
)
}
Copy the code
In the example above, we used [] as a useEffect dependency, which means that effect is executed only once after the component is mounted. We just need to set one timer, setInterval per second and help us set count + 1, right? That's what we need, isn't it
UseEffect () {useEffect (); useEffect (); useEffect (); useEffect (); useEffect (); useEffect (); useEffect (); useEffect (); useEffect
When useEffect is executed for the first time, setCount(0 + 1) is set to 1, and then setCount(1 + 1) is set 1s later. The count = 2? No, React will automatically ignore the second update and every subsequent update. Since we use [] as a dependency, this means that effect will only be executed once. The second time, count will always be 1.
Ok, so we pass in count as a dependency
useEffect(() => {
// Set the timer to execute setCount once per second
const timer = setInterval(() => {
setCount(count + 1)
}, 1000).
return() = > {
clearInterval(timer)
}
}, [count])
Copy the code
This is not the best solution because we know that every time the count value changes, render will be triggered, a new useEffect will be generated each time, and a new timer will be executed. But this is obviously not optimal, and it is best to avoid generating a new timer every time, because then our timer would be meaningless...
useEffect(() => {
// Set the timer to execute setCount once per second
const timer = setInterval(() => {
setCount(count => count + 1)
}, 1000).
return() = > {
clearInterval(timer)
}
}, [])
Copy the code
As you can see, we take count out of the dependency and setCount (count => count+1) to tell React that we only need count+1 and we don't need to read it because React knows the current value of count. UseEffect can be used only once. This is also the way setState is written. The parameter of the function is the latest state value.
UseEffect works with the useReducer?
This is the ultimate useEffect technique, and it is widely used... Using the above example, let's rewrite it with useReducer
function Demo() {
const initState = { count: 0 }
const [state, dispatch] = useReducer(reducer, initState)
function reducer(state, action) {
switch(action.type) {
case 'increment' :
return {count: state.count + 1}
default:
throw new Error('Type does not exist... ')
}
}
useEffect(() => {
// Set the timer to execute setCount once per second
const timer = setInterval(() => {
dispatch({type: 'increment'})
}, 1000).
return() = > {
clearInterval(timer)
}
}, [dispatch]) // Here we rely on dispatch
return (
<div>
<h2>{count}</h2>
<button onClick={() => setCount(count + 1)}>count++</button>
</div>
)
}
Copy the code
You might ask me, what's the advantage of writing this way? In fact, we use useReducer to describe the behavior through action to achieve the separation of state and behavior. The advantages of this writing method can be well reflected in multiple dependencies.
One final mystery: Why dispatch could be omitted? In fact, the above example works even if we take dispatch out of the dependency, and Effect only executes once. So amazing? How does Effect know what we're doing? In fact, React will help us remember various actions of Dispatch and get the latest count. These operations happen inside React and do not need to be placed in effect.
UseMemo and useCallback (Performance tuning)?
UseCallback (cache function)
const memoizedSetCount = useCallback(
() = > {
setCount(count + 1)
},
[count],
);
Copy the code
Passing the inline callback function and an array of dependencies as arguments to useCallback returns the cached version of the callback function, which is updated only when a dependency changes.
UseMemo (Cached values: Calculated attributes similar to Vue)
const memoizedCount = useMemo(() => {
const doubleCount = count * 2
}, [count]);
Copy the code
UseMemo passes the "create" function and the dependency array as arguments, and it recalculates the cache value only when a dependency changes. This optimization helps avoid costly calculations every time you render.