Last active
April 14, 2020 09:15
-
-
Save myagoo/9858781d55ac52b515b78f5844bfecf1 to your computer and use it in GitHub Desktop.
Presentations (in french) about advanced patterns and gotchas with react hooks
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Hooks | |
* Patterns avancés et pièges à éviter | |
* | |
* Par Benjamin Millagou <myagoo> | |
*/ | |
/** | |
* useState | |
*/ | |
/** | |
* Allégez vos renders | |
* avec l'initial state lazy evaluation | |
*/ | |
/** | |
* Sans lazy evaluation | |
* https://codesandbox.io/embed/vigorous-resonance-8ytp5?autoresize=1&expanddevtools=1&fontsize=16&hidenavigation=1&theme=dark | |
*/ | |
const [value, setValue] = useState(bigComputation(props)) | |
// bigComputation(props) sera éxécuté à chaque render 👎 | |
/** | |
* Avec lazy evaluation | |
* https://codesandbox.io/embed/lucid-clarke-gtw8v?autoresize=1&expanddevtools=1&fontsize=16&hidenavigation=1&theme=dark | |
*/ | |
const [value, setValue] = useState(() => bigComputation(props)) | |
// bigComputation(props) ne sera éxécuté qu'une fois 👍 | |
/** | |
* Nettoyer ses "dépendances" | |
* grâce à la forme fonctionnelle du state updater | |
*/ | |
/** | |
* Qu'est ce qui ne va pas avec ce code ? | |
* https://codesandbox.io/embed/great-fermat-6ephm?autoresize=1&expanddevtools=1&fontsize=16&hidenavigation=1&theme=dark | |
*/ | |
const [count, setCount] = useState(0) | |
useEffect(() => { | |
const id = setInterval(() => { | |
setCount(count + 1); | |
}, 1000); | |
return () => clearInterval(id); | |
}, []); | |
// rules-of-hooks n'est pas content, à juste titre 😰 | |
/** | |
* Premier fix | |
* https://codesandbox.io/embed/sweet-mccarthy-5flpk?autoresize=1&expanddevtools=1&fontsize=16&hidenavigation=1&theme=dark | |
*/ | |
const [count, setCount] = useState(0) | |
useEffect(() => { | |
const id = setInterval(() => { | |
setCount(count + 1); | |
}, 1000); | |
return () => clearInterval(id); | |
}, [count]); | |
// rules-of-hooks est content, mais on peut faire mieux 😐 | |
/** | |
* Version qui utilise la forme fonctionnelle du state updater | |
* https://codesandbox.io/embed/crazy-bassi-4lpeg?autoresize=1&expanddevtools=1&fontsize=16&hidenavigation=1&theme=dark | |
*/ | |
useEffect(() => { | |
const id = setInterval(() => { | |
setCount((prevCount) => prevCount + 1); | |
}, 1000); | |
return () => clearInterval(id); | |
}, []); | |
// Tout le monde est content 😀 | |
/** | |
* Le cas this.setState(value, callback) | |
* ^^^^^^^^ | |
*/ | |
/** | |
* Avant | |
* | |
* https://codesandbox.io/embed/holy-darkness-1snrm?autoresize=1&expanddevtools=1&fontsize=16&hidenavigation=1&theme=dark | |
*/ | |
class Counter extends React.Component { | |
state = { | |
count: 0 | |
} | |
handleIncrement(){ | |
this.setState( | |
({count}) => ({count: count + 1}), | |
() => alert(this.state.count) // Callback éxécutée lorsque l'update du state aura été appliqué | |
) | |
} | |
render(){ | |
<button onClick={this.handleIncrement}>Increment</button> | |
} | |
} | |
/** | |
* Première réécriture | |
* | |
* https://codesandbox.io/embed/quirky-monad-i6tqd?autoresize=1&expanddevtools=1&fontsize=16&hidenavigation=1&theme=dark | |
*/ | |
const Counter = () => { | |
const [count, setCount] = useState(0) | |
const handleIncrement = () => { | |
setCount( | |
(prevCount) => prevCount + 1, | |
() => alert(count) // REACT PAKONTAN | |
) | |
} | |
return <button onClick={handleClick}>Increment</button> | |
} | |
/** | |
* Fixed | |
* | |
* https://codesandbox.io/embed/funny-kapitsa-dgskq | |
*/ | |
const Counter = () => { | |
const [count, setCount] = useState(0) | |
const handleIncrement = () => { | |
setCount((prevCount) => prevCount + 1) | |
} | |
useEffect(() => { | |
alert(count) | |
}, [count]) | |
return <button onClick={() => handleClick()}>-</button> | |
} | |
/** | |
* useCallback | |
*/ | |
/** | |
* this.handleClick.bind(this) !== const handleClick = useCallback(() => {}) | |
* | |
* useCallback sert à avoir une référence stable d'une fonction | |
* | |
* Si la fonction n'est pas utilisée en tant que dep d'un autre hook, | |
* useCallback est certainement superflu | |
* | |
* https://codesandbox.io/embed/agitated-gauss-h5ium?autoresize=1&expanddevtools=1&fontsize=16&hidenavigation=1&theme=dark | |
* | |
* https://codesandbox.io/embed/focused-lake-7qgwr?autoresize=1&expanddevtools=1&fontsize=16&hidenavigation=1&theme=dark | |
*/ | |
/** | |
* useRef | |
* | |
* | |
* Une ref se comporte comme une "variable d'instance" de Class Component | |
* | |
* 🚨 muter la valeur courante d'une ref ne provoque pas de rerender | |
* | |
* | |
* useRef to the rescue | |
* | |
* https://codesandbox.io/embed/rough-dream-essy0?autoresize=1&expanddevtools=1&fontsize=16&hidenavigation=1&theme=dark | |
* | |
* | |
*/ | |
/** | |
* useEffect | |
*/ | |
/** | |
* "A chaque render ses effets" | |
* - Moi, maintenant. | |
* | |
* | |
* | |
* https://codesandbox.io/embed/damp-wind-fwn3f?autoresize=1&expanddevtools=1&fontsize=16&hidenavigation=1&theme=dark | |
* | |
* | |
* Pour aller plus loin, lire | |
* https://overreacted.io/a-complete-guide-to-useeffect | |
*/ | |
/** | |
* useMemo | |
*/ | |
/** | |
* "Vous pouvez vous appuyer sur useMemo comme un moyen d’optimiser les performances, | |
* mais pas comme une garantie sémantique" | |
* - La doc React | |
* | |
* Même si vos dépendances ne changent pas, | |
* il se peut que, un jour, react recompute votre valeur !!! | |
* | |
* Soyez IDEMPOTENT 😎 | |
* Sinon attendez vous à des bugs de l'espace | |
*/ | |
/** | |
* useLayoutEffect | |
*/ | |
/** | |
* Pour déclencher un effet qui aura lieu | |
* APRÈS les mutations dom du render | |
* https://codesandbox.io/embed/loving-snow-yvk9j?autoresize=1&expanddevtools=1&fontsize=16&hidenavigation=1&theme=dark | |
*/ | |
const triggerRef = useRef(); | |
const [clientRect, setClientRect] = useState(); | |
... | |
useLayoutEffect(() => { | |
setClientRect(triggerRef.current.getBoundingClientRect()); | |
}, [triggerRef && triggerRef.current]); | |
... | |
<button ref={triggerRef}>Menu</button> | |
/** | |
* Disclaimer sur rules-of-hooks | |
* | |
* https://codesandbox.io/embed/suspicious-meitner-gke6z?autoresize=1&expanddevtools=1&fontsize=16&hidenavigation=1&theme=dark | |
* | |
* TL;DR | |
* Ne faites pas comme moi, | |
* n'y accordez pas une confiance aveugle 😎 | |
*/ | |
/** | |
* En conclusion | |
* | |
* > Lire l'article de Dan Abramov sur useEffect | |
* | |
* > On a vite fait de prendre nos Functional Component pour des Class Component | |
* | |
*/ | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment