existReact Hooks
When using third-party libraries in , many people will write this (referring to me):
const [count, setCount] = useState(0); useEffect(() => { const library = new Library(); ("click", () => { (count); // Can't get the latest count }); }, []);
Writing this will cause problems:
It will only bind events when this component is loaded, if other events are usedstate
, then when this state changes, the latest one cannot be obtained in the event.state
You will think, I'llstate
Put it in dependencies:
const [count, setCount] = useState(0); useEffect(() => { const library = new Library(); // The click event will be repeatedly bound ("click", () => { (count); }); }, [count]);
Doing so will lead to new problems:click
Events will be repeatedly bound
At this time, you say I'll uninstall it firstclick
Event, in the binding event:
const [count, setCount] = useState(0); useEffect(() => { const library = new Library(); ("click", handleClick); return () => { // Events cannot be uninstalled, and they will still be repeatedly bound handleClick && ("click", handleClick); }; }, [count]); const handleClick = () => { (count); };
You were surprised to find that if you could not uninstall the previous event, you would still repeatedly bind the event.
How to solve this problem?
Events using addEventListener instead of third-party libraries
Used hereaddEventListener
Instant code of events in place of third-party libraries
const Test = (props) => { const ref = useRef(); const [count, setCount] = useState(0); useEffect(() => { const handleClick = (event) => { ("clicked"); ("count", count); }; const element = ; ("click", handleClick); return () => { ("click", handleClick); }; }, []); const onClickIncrement = () => { setCount(count + 1); }; return ( <> <h2>Test</h2> <button onClick={onClickIncrement}>Click +1</button> <div>count: {count}</div> <button ref={ref}>Click Test Button</button> </> ); };
Method 1: state changes, uninstall/bind events
Willstate
Put it in dependencies and it needs to be solvedstate
The issue of repeated binding of events during changes
Solve the problem of repeated binding of events, the first thing that comes to mind is event uninstallation
You'll easily think of writing like this
useEffect(() => { handleClick && ("click", handleClick); ("click", handleClick); }, [count]); const handleClick = () => { (count); };
This isReact Hooks
is a pit,state
After the change,handleClick
The event function will be redeclared, newhandleClick
And the previous onehandleClick
Not an event function, resulting inremoveEventListener
The removed event function is not the previous event function
Then you will think of me,handleClick
Add oneuseCallback
useEffect(() => { handleClick && ("click", handleClick); ("click", handleClick); }, [count]); const handleClick = useCallback(() => { (count); }, []);
If you write this way, you will still have the same problem: if the dependency is an empty array, you will not get the latest one.state
; Put in dependenciesstate
,state
After the change, it is not the same event function, and events cannot be removed.
How to solve this problem?
Save the event function as a state:
- when
count
When changing, mount the event and save the event function asstate
- when
When changing, in
useEffect return
The event function before uninstalling (the closure is used here)
Specific code:
const Test = () => { const ref = useRef(); const [count, setCount] = useState(0); const [eventFn, setEventFn] = useState({ fn: null }); useEffect(() => { mountEvent(); }, [count]); const mountEvent = () => { if (!) return; // && ("click", ); // If you don't understand below, you can also write this way ("click", handleClick); setEventFn({ fn: handleClick }); }; useEffect(() => { return () => { && ("click", ); // Here is a closure, and choose any of the comments above }; }, []); const handleClick = () => { (count); }; const onClickIncrement = () => { setCount(count + 1); }; return ( <> <h2>Test</h2> <button onClick={onClickIncrement}>Click +1</button> <div>count: {count}</div> <button ref={ref}>Click Test Button</button> </> ); };
Method 2: Uninstall events using closures
Using closures, you can simplify the method
const Test = () => { const ref = useRef(); const [count, setCount] = useState(0); useEffect(() => { const element = ; ("click", handleClick); return () => { ("click", handleClick); }; }, [count]); const handleClick = () => { (count); }; const onClickIncrement = () => { setCount(count + 1); }; return ( <> <h2>Test</h2> <button onClick={onClickIncrement}>Click +1</button> <div>count: {count}</div> <button ref={ref}>Click Test Button</button> </> ); };
useEffect return
The variables in the use closures, which is difficult to understand when I first learned
Method 3: Use ref to save state
ref
Although the saved data cannot be used for page rendering, it can be used asstate
Backup, instate
Updated when changesref
You can get the latest in the event functionstateRef
const Test = () => { const ref = useRef(); const [count, setCount] = useState(0); const countRef = useRef(count); useEffect(() => { = count; }, [count]); useEffect(() => { const element = ; ("click", handleClick); }, []); const handleClick = () => { (); }; const onClickIncrement = () => { setCount(count + 1); }; return ( <> <h2>Test</h2> <button onClick={onClickIncrement}>Click +1</button> <div>count: {count}</div> <button ref={ref}>Click Test Button</button> </> ); };
Optimize state manual maintenance
There is a problem with the above three methods.state
Need manual maintenance
How to optimize this step?
Method 1 and method 2, the optimization method is the same: the dependency iscount
Change tostate
const [state, setState] = useState({ count: 0 }); useEffect(() => { // ... }, [state]);
Method 3 optimization is to usestateRef
Saveref
Object, whenstate
When changing, traversestate
GivestateRef
Assignment
Used in event functionsstateRef
const [state, setState] = useState({ count: 0 }); const stateRef = useRef({}); useEffect(() => { (state).forEach((key) => { [key] = state[key]; }); }, [state]);
This is the end of this article about how to use addEventListener elegantly in React. For more related React addEventListener content, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!