Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
The biggest lies about React Hooks
Search
Fakiolas Marios
June 24, 2019
Programming
0
31
The biggest lies about React Hooks
What problems React Hooks do really solve?
Fakiolas Marios
June 24, 2019
Tweet
Share
More Decks by Fakiolas Marios
See All by Fakiolas Marios
Modern e2e testing for complex web applications with Cypress.io
fakiolinho
0
23
Get rid of controllers in angular 1.5.x start using component directives
fakiolinho
0
110
Web components are the future of the web - Take advantage of new web technologies using PolymerJS
fakiolinho
0
130
Angular 2 - How we got here?
fakiolinho
0
57
Other Decks in Programming
See All in Programming
AI Agent の開発と運用を支える Durable Execution #AgentsInProd
izumin5210
7
2.3k
Oxlintはいいぞ
yug1224
5
1.3k
並行開発のためのコードレビュー
miyukiw
0
220
humanlayerのブログから学ぶ、良いCLAUDE.mdの書き方
tsukamoto1783
0
200
「ブロックテーマでは再現できない」は本当か?
inc2734
0
1k
AI によるインシデント初動調査の自動化を行う AI インシデントコマンダーを作った話
azukiazusa1
1
740
AIフル活用時代だからこそ学んでおきたい働き方の心得
shinoyu
0
140
AWS re:Invent 2025参加 直前 Seattle-Tacoma Airport(SEA)におけるハードウェア紛失インシデントLT
tetutetu214
2
110
疑似コードによるプロンプト記述、どのくらい正確に実行される?
kokuyouwind
0
390
要求定義・仕様記述・設計・検証の手引き - 理論から学ぶ明確で統一された成果物定義
orgachem
PRO
1
140
Apache Iceberg V3 and migration to V3
tomtanaka
0
160
AgentCoreとHuman in the Loop
har1101
5
240
Featured
See All Featured
AI Search: Where Are We & What Can We Do About It?
aleyda
0
7k
Automating Front-end Workflow
addyosmani
1371
200k
Beyond borders and beyond the search box: How to win the global "messy middle" with AI-driven SEO
davidcarrasco
1
53
Leading Effective Engineering Teams in the AI Era
addyosmani
9
1.6k
svc-hook: hooking system calls on ARM64 by binary rewriting
retrage
1
100
Marketing to machines
jonoalderson
1
4.6k
Testing 201, or: Great Expectations
jmmastey
46
8k
Self-Hosted WebAssembly Runtime for Runtime-Neutral Checkpoint/Restore in Edge–Cloud Continuum
chikuwait
0
330
BBQ
matthewcrist
89
10k
YesSQL, Process and Tooling at Scale
rocio
174
15k
Color Theory Basics | Prateek | Gurzu
gurzu
0
200
Mind Mapping
helmedeiros
PRO
0
87
Transcript
The biggest lies about The biggest lies about React Hooks
React Hooks
[email protected]
twitter.com/@fakiolinho - medium.com/@fakiolinho 1
Full-stack JavaScript lover, happy husband, proud father Software Engineering Manager
/ Frontend Head at omilia.com Fakiolas Marios
[email protected]
- twitter.com/@fakiolinho - medium.com/@fakiolinho Workshops Tutor at 2hog.codes 2
React Hooks React Hooks React 16.8 - 6/2/2019 3 .
1
React Hooks React Hooks React 16.8 - 6/2/2019 3 .
2
React Hooks React Hooks were created so we can win
were created so we can win the fight against evil classes the fight against evil classes 4 . 1
React Hooks React Hooks were created so we can win
were created so we can win the fight against evil classes the fight against evil classes 4 . 2
React Hooks React Hooks so why then? 5
React Hooks React Hooks were created in order to re-use
logic without nesting deeper in the components tree ...i am looking at you HOCs and render props 6
As an architect, you are asked to design a brand
new project 7
Your go-to technology is ReactJS 8
Specs indicate that lots of forms will be implemented 9
You decide to go with controlled components 10 . 1
class Form extends React.Component { state = { field: '',
}; handleSubmit = data => axios.post('/url', data); render() { return ( <form onSubmit={this.handleSubmit}> <input value={this.state.field} onChange={e => ( this.setState({ field: e.target.value }) )} /> </form> ); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 A class component is required 10 . 2
state = { field: '', }; <input value={this.state.field} onChange={e =>
( this.setState({ field: e.target.value }) )} class Form extends React.Component { 1 2 3 4 5 handleSubmit = data => axios.post('/url', data); 6 7 render() { 8 return ( 9 <form onSubmit={this.handleSubmit}> 10 11 12 13 14 15 /> 16 </form> 17 ); 18 } 19 Because you need internal state 10 . 3
...and you ship it 11
...the project should support longer forms with multiple fields 12
Who hates some old-fashioned copy-paste right? 13 . 1
<form onSubmit={this.handleSubmit}> <input value={this.state.field1} onChange={e => ( this.setState(prevState => ({
...prevState, field1: e.target.value })) )} /> <input value={this.state.field2} onChange={e => ( this.setState(prevState => ({ ...prevState, field2: e.target.value })) )} /> ... </form> 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 13 . 2
...and you ship it 14
...the project should support complex forms with different types of
fields 15
Copy and paste won't do the job this time 16
maybe we can create some re-usable components and move faster
17
const Field = ({ name="field", value="", type="text", onChange, }) =>
( <input name={name} type={type} value={value} onChange={onChange} /> ); 1 2 3 4 5 6 7 8 9 10 11 12 13 Let's create our own Field component 18 . 1
class Form extends React.Component { state = { field: '',
}; handleChange = e => this.setState({ [e.target.name]: e.target.value, }); handleSubmit = e => { e.preventDefault(); axios.post('/url', this.state); }; render() { return ( <form onSubmit={this.handleSubmit}> <Field name="field" value={this.state.field} onChange={this.handleChange} /> </form> ); } } 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 Refactor our Form component 18 . 2
handleChange = e => this.setState(prevState => ({ ...prevState, [e.target.name]: e.target.value,
})); <Field name="field" value={this.state.field} onChange={this.handleChange} /> <Field.../> class Form extends React.Component { 1 state = { 2 field: '', 3 }; 4 5 6 7 8 9 10 handleSubmit = e => ...; 11 12 render() { 13 return ( 14 <form onSubmit={this.handleSubmit}> 15 16 17 18 19 20 21 </form> 22 ); 23 } 24 } 25 Field component can be re-used easily 18 . 3
What about forms logic? It is not that re-usable 19
. 1
state = { field: '', }; handleChange = e =>
this.setState({ [e.target.name]: e.target.value, }); handleSubmit = e => { e.preventDefault(); axios.post('/url', this.state); }; class Form extends React.Component { 1 2 3 4 5 6 7 8 9 10 11 12 13 14 render() { 15 return ( 16 <form onSubmit={this.handleSubmit}> 17 <Field 18 name="field" 19 value={this.state.field} 20 onChange={this.handleChange} 21 /> 22 </form> 23 ); 24 } 25 } 26 Repeated logic across all forms 19 . 2
Let's provide a wrapper that will bring this kind of
logic anywhere we like 20
const Form = () => { const initialValues = {
field: "", }; const handleSubmit = data => axios.post('/url', data); return ( <FormHandler initialValues={initialValues} onSubmit={handleSubmit}> {({ values, onChange, onSubmit }) => ( <form onSubmit={onSubmit}> <Field value={values.field} name="field" onChange={onChange} /> </form> )} </FormHandler> ); }; 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 Let's use render props pattern 21 . 1
<FormHandler initialValues={initialValues} onSubmit={handleSubmit}> {({ values, onChange, onSubmit }) => (
)} </FormHandler> const Form = () => { 1 const initialValues = { 2 field: "", 3 }; 4 5 const handleSubmit = data => axios.post('/url', data); 6 7 return ( 8 9 10 <form onSubmit={onSubmit}> 11 <Field 12 value={values.field} 13 name="field" 14 onChange={onChange} 15 /> 16 </form> 17 18 19 ); 20 }; 21 Re-usable logic by using render props 21 . 2
<form onSubmit={onSubmit}> value={values.field} onChange={onChange} const Form = () => {
1 const initialValues = { 2 field: "", 3 }; 4 5 const handleSubmit = data => axios.post('/url', data); 6 7 return ( 8 <FormHandler initialValues={initialValues} onSubmit={handleSubmit}> 9 {({ values, onChange, onSubmit }) => ( 10 11 <Field 12 13 name="field" 14 15 /> 16 </form> 17 )} 18 </FormHandler> 19 ); 20 }; 21 Re-usable logic by using render props 21 . 3
export default class FormHandler extends React.Component { state = this.props.initialValues;
handleChange = e => { e.persist(); this.setState(prevState => ({ ...prevState, [e.target.name]: e.target.value })); }; handleSubmit = e => { e.preventDefault(); this.props.onSubmit(this.state); }; render() { return this.props.children({ values: this.state, onChange: this.handleChange, onSubmit: this.handleSubmit }); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 Re-usable logic by using render props 21 . 4
Neat, our forms are looking good 22
...the project needs to provide a confirmation step for all
the forms that have a destructive purpose 23
We need a confirmation step for certain forms 24 .
1
Should we add a prop named "shouldDoubleCheck" to prevent forms
submission at will? <FormHandler shouldDoubleCheck initialValues={initialValues} onSubmit={onSubmit} > ... </FormHandler> 1 2 3 4 5 6 7 24 . 2
handleSubmit = e => { e.preventDefault(); if (this.props.shouldDoubleCheckout) { const
answer = window.confirm('Are you really sure about this?'); if (answer) { this.props.onSubmit(this.state); } } else { this.props.onSubmit(this.state); } }; 1 2 3 4 5 6 7 8 9 10 11 12 13 Check for "shouldDoubleCheck" across all forms submissions 24 . 3
This logic will run across all forms right? Only a
few really will use it 25
We need another wrapper that will introduce this confirmation logic
26
<FormHandler initialValues={initialValues} onSubmit={handleSubmit}> {({ values, onChange, onSubmit }) => (
<DoubleCheckHandler onSubmit={onSubmit}> {({ onSubmitWithDoubleCheck }) => ( <form onSubmit={onSubmitWithDoubleCheck}> <Field value={values.field} name="field" label="Field" onChange={onChange} /> </form> )} </DoubleCheckHandler> )} </FormHandler> 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Introduce "DoubleCheckHandler" wrapper 27 . 1
<DoubleCheckHandler onSubmit={onSubmit}> {({ onSubmitWithDoubleCheck }) => ( <form onSubmit={onSubmitWithDoubleCheck}> )}
</DoubleCheckHandler> <FormHandler initialValues={initialValues} onSubmit={handleSubmit}> 1 {({ values, onChange, onSubmit }) => ( 2 3 4 5 <Field 6 value={values.field} 7 name="field" 8 label="Field" 9 onChange={onChange} 10 /> 11 </form> 12 13 14 )} 15 </FormHandler> 16 Introduce "DoubleCheckHandler" wrapper 27 . 2
const DoubleCheckHandler = ({ onSubmit, children }) => { const
handleSubmitWithDoubleCheck = e => { e.preventDefault(); const answer = window.confirm("Are you really sure about this?"); if (answer) { onSubmit(e); } }; return children({ onSubmitWithDoubleCheck: handleSubmitWithDoubleCheck }); }; 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 No "shouldDoubleCheck" prop is needed in DoubleCheckHandler 27 . 3
Neat, our forms are looking good Let's share all this
effort with the teammates 28
No-one seems to be excited 29
"This is not that readable. How are we going to
maintain these components?" 30 . 1
<FormHandler initialValues={initialValues} onSubmit={handleSubmit}> {({ values, onChange, onSubmit }) => (
<DoubleCheckHandler onSubmit={onSubmit}> {({ onSubmitWithDoubleCheck }) => ( ... )} </DoubleCheckHandler> )} </FormHandler> 1 2 3 4 5 6 7 8 9 30 . 2
"What about all these wrapping components? Components tree seems pretty
ugly" 31 . 1
31 . 2
"What about testing? How deep we should go to find
and tweak a Field?" 32 . 1
We need to use full mount with enzyme or many
dives with shallow testing 32 . 2
Obviously this solution isn't that cool 33
Time to jump 1 year ahead and reach Hooks era
34 . 1
const Form = () => { const initialValues = {
field: '', }; const handleSubmit = data => axios.post('/url', data); // We need a mechanism to get: // 1. updated values // 2. re-usable onChange handler // 3. re-usable onSubmit handler return ( <form onSubmit={onSubmit}> <Field value={values.field} name="field" onChange={onChange} /> </form> ); }; 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 Let's take 1 big breath and start from the very start 34 . 2
const { values, onChange, onSubmit, } = useForm(initialValues, handleSubmit); const
Form = () => { 1 const initialValues = { 2 field: '', 3 }; 4 5 const handleSubmit = data => axios.post('/url', data); 6 7 8 9 10 11 12 13 return ( 14 <form onSubmit={onSubmit}> 15 <Field 16 value={values.field} 17 name="field" 18 onChange={onChange} 19 /> 20 </form> 21 ); 22 }; 23 A custom Hook is all we need 34 . 3
const useForm = (initialValues = {}, onSubmit) => { const
[values, setValues] = React.useState(initialValues); const handleChange = e => { e.persist(); setValues({ ...values, [e.target.name]: e.target.value }); }; const handleSubmit = e => { e.preventDefault(); onSubmit(values); }; return { values, onChange: handleChange, onSubmit: handleSubmit }; }; 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 We can create a custom Hook with some internal state 34 . 4
const [values, setValues] = React.useState(initialValues); const useForm = (initialValues =
{}, onSubmit) => { 1 2 3 const handleChange = e => { 4 e.persist(); 5 setValues({ 6 ...values, 7 [e.target.name]: e.target.value 8 }); 9 }; 10 11 const handleSubmit = e => { 12 e.preventDefault(); 13 onSubmit(values); 14 }; 15 16 return { values, onChange: handleChange, onSubmit: handleSubmit }; 17 }; 18 "useState" Hook does all the dirty work 34 . 5
const Form = () => { const initialValues = {
field: '', }; const handleSubmit = data => axios.post('/url', data); const { values, onChange, onSubmit, } = useForm(initialValues, handleSubmit); return ( <form onSubmit={onSubmit}> <Field value={values.field} name="field" onChange={onChange} /> </form> ); }; 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 Much more readable code with Hooks 35 . 1
const { values, onChange, onSubmit, } = useForm(initialValues, handleSubmit); const
{ onDoubleCheckSubmit } = useDoubleCheck(onSubmit); <form onSubmit={onDoubleCheckSubmit}> const Form = () => { 1 const initialValues = { 2 field: '', 3 }; 4 5 const handleSubmit = data => axios.post('/url', data); 6 7 8 9 10 11 12 13 14 return ( 15 16 <Field 17 value={values.field} 18 name="field" 19 onChange={onChange} 20 /> 21 </form> 22 ); 23 }; 24 Much more readable code with Hooks 35 . 2
Cleaner components tree with Hooks 36
No more deep dive to test elements behaviour with Hooks
37
...everyone is happy, so you ship it 38
Code re-usability with some simple functions and not complex patterns
Much more readable code No more garbage in components tree React Hooks pros React Hooks pros 39
We still need classes for a couple cases: Error Boundaries
getSnapshotBeforeUpdate we cannot use Hooks directly inside classes Do Hooks replace classes? Do Hooks replace classes? 40
Questions 41