Skip to content

Instantly share code, notes, and snippets.

@myagoo
Last active April 14, 2020 09:15
Show Gist options
  • Save myagoo/9858781d55ac52b515b78f5844bfecf1 to your computer and use it in GitHub Desktop.
Save myagoo/9858781d55ac52b515b78f5844bfecf1 to your computer and use it in GitHub Desktop.
Presentations (in french) about advanced patterns and gotchas with react hooks
/**
* 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