👉 React-hooks (2) Event-binding side effects
Once again: why use useCallback to cache onMouseMove? Because this function is a useEffect dependency and it is a reference value, each re-render is a new function. If the function does not change, there is no need to generate a new re-render every time, which is the main purpose of useCallbak.
Here is an extension of the hooks dependencies. I’ll start with some of the HashRouter code I restored myself:
export default function HashRouter(props) { let locationState = null; const [location, setlocation] = useState({ pathname: window.location.hash.slice(1) || "/", state: locationState }); Const handleHashChange = useCallback(() => {console.log(' execute the function inside useCallback ') // Whenever hashchange executes setLocation ({... The location, the pathname: window. The location. The hash. Slice (1), / / new pathname value does not depend on a pathname state: locationState})}, []); UseEffect (() => {console.log(' execute useEffect function ')) // Only prints window.addeventListener ('hashchange', handleHashChange) return () => { window.removeEventListener('hashchange', handleHashChange) } }, []); Const val = {location, history: {push: const val = {location, history: {push: to => { if (typeof to === 'object') { let { pathname, state } = to; window.location.hash = pathname; locationState = state; } else { window.location.hash = to; } } } } return <ReactRouterContext.Provider value={val}> { props.children } </ReactRouterContext.Provider> }Copy the code
Look at the comments section of the code. Yes, I didn’t add dependencies in either useCallback or useEffect above. In fact, this is not good, can cause confusion and confusion.
On the other hand, it does what the cache function && was designed to do: bind the hashchange event only once && switch pages properly during hashchange:
- Implement the cache handleHashChange method. It is only generated during the first render, after which re-render returns cached functions. Why make the dependency null? Because it really doesn’t have the required dependencies. The setLocation method inside handleHashChange does not depend on the variable returned by useState. Browser events are special in that they must be emitted after binding unless the page is unloaded or unbound manually.
- UseEffect is only executed once, but it only needs to be bound to the hashchange function on the first execution. It does not need to be re-bound and unbound every time re-render is performed.
When I first compared the article quoted at the beginning of the article with my own code, I was also confused. After careful screening, I found that the setLocation was caused by the external acquisition of the new value. This is why the dependency is empty and can still be refreshed.
Let’s introduce a control group and see that this is true:
export default function HashRouter(props) { let locationState = null; const [location, setlocation] = useState({ pathname: window.location.hash.slice(1) || "/", state: locationState }); Const [count, setcount] = useState({// +++++ add +++++ number: 0}) const handleHashChange = useCallback(() => {setcount({number: count.number+1 // +++++ add this paragraph +++++}); setlocation({ ... location, pathname: window.location.hash.slice(1), state: locationState }) }, []); useEffect(() => { window.addEventListener('hashchange', handleHashChange) return () => { window.removeEventListener('hashchange', handleHashChange) } }, []); console.log('count--------', count); // +++++ add the section +++++ // to find that the count only increases to 1, and then stays the same. Because the dependency for handleHashChange is empty. The dependency increments count by placing count. console.log('location', location); // +++++ adds the section +++++ // this changes because it evaluates to the new value from window.location.hash rather than relying on the previous value const val = {location, history: { push: to => { if (typeof to === 'object') { let { pathname, state } = to; window.location.hash = pathname; locationState = state; } else { window.location.hash = to; } } } } return <ReactRouterContext.Provider value={val}> { props.children } </ReactRouterContext.Provider> }Copy the code
Take a look at the printed result of executing this code:
This result is the same as the result of the article recommended above.