1. useCallback()
-- React.memo() vẫn bị render lại trong 1 số trường hợp
-- useCallback() sinh ra để giải quyết nó :3
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>ReactJS</title> </head> <body> <div id="root"></div> <script crossorigin src="https://unpkg.com/react@17/umd/react.development.js"></script> <script crossorigin src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script> <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script> <script type="text/babel"> const { useState, useCallback, memo } = React; function App() { const [count, setCount] = useState(0); const handleHeader = () => { } // dependencies của useCallback giống với useEffect() // []: chạy lần đầu render // null: luôn luôn chạy // [x,y]: chạy khi biến x,y thay đổi const handleFooter = useCallback( () => {}, [], ) return ( <div> <Header1 handleHeader={handleHeader} /> <MemoHeader2 handleHeader={handleHeader} /> <MemoFooter handleFooter={handleFooter} /> <p>Current: {count}</p> <button onClick={() => { setCount(count + 1) }}>Increase</button><br /> </div> ) } // Mỗi lần App re-render ==> đương nhiên Header cũng bị re-render theo const Header1 = (props) => { console.log("Header1 render"); return <p>Header1</p> } // Mỗi lần App re-render ==> Header2 cũng bị re-render theo mặc dù đã dùng React.memo() // Lý do: mỗi lần App re-render thì props.handleHeader đã hiểu là 1 biến khác => nên React.memo() không có tác dụng const Header2 = (props) => { console.log("Header2 render"); return <p>Header2</p> } const MemoHeader2 = React.memo(Header2); // Khi sử dụng useCallback(), props.handleFooter không thay đổi địa chỉ, vẫn hiểu là biến cũ // Như vậy: props không thay đổi + dùng memo ==> Component không bị re-render const Footer = (props) => { console.log("Footer render"); return <p>Footer</p> } const MemoFooter = React.memo(Footer); ReactDOM.render( <App />, document.getElementById('root') ); </script> </body> </html> |
2. Ví dụ trường hợp Call API
1. Khi không có useCallback()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>ReactJS</title> </head> <body> <div id="app"></div> <script crossorigin src="https://unpkg.com/react@17/umd/react.development.js"></script> <script crossorigin src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script> <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script> <script type="text/babel"> const { useState, useEffect, useCallback } = React; function App(props) { const [count, setCount] = useState(0); //1. Mỗi lần click Count++ => App render lại // ==> tạo ra 1 biến getFakeData khác nhau const getFakeData = (type) => { return `https://reqres.in/api/${type}` } return ( <div> <button onClick={() => { setCount(count + 1) }}>Count++</button> <p>Count: {count}</p> <Content getFakeData={getFakeData} /> </div> ) } function Content({ getFakeData }) { const [users, setUsers] = useState([]); const [loading, setLoading] = useState(false) //2. getFakeData là 1 props, đã bị thay đổi sau mỗi lần App render // Vì thế cả đoạn dưới đây đều bị thay đổi theo (theo tính chất useEffect) useEffect(() => { setLoading(true); setTimeout(() => { let url = getFakeData('users') fetch(url) .then(res => res.json()) .then(res => { setUsers(res.data) }) .catch(err => { console.log(err); }) .finally(() => { setLoading(false); }) }, 1000); }, [getFakeData]) return (<div>{loading == true ? <p>Loading...</p> : <p>{JSON.stringify(users)}</p>}</div>) } ReactDOM.render( <App />, document.getElementById('app') ); </script> </body> </html> |
Khi sử dụng useCallback()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>ReactJS</title> </head> <body> <div id="app"></div> <script crossorigin src="https://unpkg.com/react@17/umd/react.development.js"></script> <script crossorigin src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script> <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script> <script type="text/babel"> const { useState, useEffect, useCallback } = React; function App(props) { const [count, setCount] = useState(0); //1. Mỗi lần click Count++ => App render lại // Khi dùng useCallback ==> getFakeData không thay đổi địa chỉ const getFakeData = useCallback( (type) => { return `https://reqres.in/api/${type}` }, [] ) return ( <div> <button onClick={() => { setCount(count + 1) }}>Count++</button> <p>Count: {count}</p> <Content getFakeData={getFakeData} /> </div> ) } function Content({ getFakeData }) { const [users, setUsers] = useState([]); const [loading, setLoading] = useState(false) console.log(getFakeData); //2. getFakeData là 1 props, không thay đổi sau mỗi lần App render (do dùng useCallback) // Vì thế cả đoạn dưới đây không hề bị run lại useEffect(() => { setLoading(true); setTimeout(() => { let url = getFakeData('users') fetch(url) .then(res => res.json()) .then(res => { setUsers(res.data) }) .catch(err => { console.log(err); }) .finally(() => { setLoading(false); }) }, 1000); }, [getFakeData]) return (<div>{loading == true ? <p>Loading...</p> : <p>{JSON.stringify(users)}</p>}</div>) } ReactDOM.render( <App />, document.getElementById('app') ); </script> </body> </html> |