Upgrade to Pro — share decks privately, control downloads, hide ads and more …

With great power comes great React Hooks!

With great power comes great React Hooks!

3261558e341cf0c94af163c3a0f06ad9?s=128

Glenn Reyes

April 05, 2019
Tweet

Transcript

  1. With great power comes great React Hooks! Glenn Reyes @glnnrys

  2. None
  3. None
  4. Glenn Reyes Freelance JavaScript Engineer @glnnrys

  5. Let's talk components!

  6. var App = React.createClass({ getInitialState: function() { return { value:

    '' }; }, onChange: function(event) { this.setState({ value: event.target.value }); }, render: function() { return ( <div> <input type="text" value={this.state.value} onChange={this.onChange} /> <p>{this.state.value}</p> </div> ); }, });
  7. [GIF?]

  8. React.createClass({ mixins: [ReactWindowMixins.OnResize], render: function() { const { window }

    = this.state; return ( <div> current window size: {window.width}, {window.height} </div> ); } });
  9. React.createClass({ mixins: [ReactWindowMixins.OnResize], // render: function() { const { window

    } = this.state; return ( <div> current window size: {window.width}, {window.he
  10. None
  11. None
  12. class App extends React.Component { state = { value: ''

    }; onChange = event => { this.setState({ value: event.target.value }); } render() { return ( <div> <input type="text" value={this.state.value} onChange={this.onChange} /> <p>{this.state.value}</p> </div> ) } }
  13. None
  14. const App = (props) => { const greeting = 'Hello';

    return <div>{greeting} {props.name}!</div>; }
  15. How do I add state to function components?

  16. Container & Presentational Components

  17. Container & Presentational Components DATA UI

  18. class MyData extends React.Component { state = { value: ''

    }; onChange = event => { this.setState({ value: event.target.value }); } render() { return ( <MyUI value={this.state.value} onChange={this.onChange} /> ) } } const MyUI = ({ value, onChange }) => ( <div> <input type="text" value={value} onChange={onChange} /> <p>{value}</p> </div> )
  19. class MyData extends React.Component { state = { value: ''

    }; onChange = event => { this.setState({ value: event.target.value }); } render() { return ( <MyUI value={this.state.value} onChange={this.onChange} /> ) } } const MyUI = ({ value, onChange }) => ( <div> <input type="text" value={value} onChange={onChange} /> <p>{value}</p> </div> ) class MyData extends React.Component { state = { value: '' }; onChange = event => { this.setState({ value: event.target.value }); } render() { return ( <MyUI value={this.state.value} onChange={this.onChange} /> ) } }
  20. Higher Order Components!

  21. const EnhancedComponent = higherOrderComponent(WrappedComponent);

  22. const withInput = Component => class InputContainer extends React.Component {

    state = { value: '' }; onChange = event => { this.setState({ value: event.target.value }); } render() { return ( <Component value={this.state.value} onChange={this.onChange} /> ) } } const App = withInput(({ value, onChange }) => ( <div> <input type="text" value={value} onChange={onChange} /> <p>{value}</p> </div> ))
  23. hoc1(hoc2(hoc3(hoc4(Component))))

  24. compose( hoc1, hoc2, hoc3, hoc4 )(Component)

  25. Thank god Render Props came around!

  26. class DataProvider extends React.Component { state = { data: null

    } componentDidMount() { fetchSomeData().then(data => this.setState({ data })); } render() { return this.props.render(data); } } <DataProvider render={data => ( <h1>Hello {data.name}</h1> )}/>
  27. class DataProvider extends React.Component { state = { data: null

    } componentDidMount() { fetchSomeData().then(data => this.setState({ data })); } render() { return this.props.render(data); } } <DataProvider render={data => ( <h1>Hello {data.name}</h1> )}/> class DataProvider extends React.Component { state = { data: null } componentDidMount() { fetchSomeData().then(data => this.setState({ data })); } render() { return this.props.render(data); } }
 
 
 
 

  28. class DataProvider extends React.Component { state = { data: null

    } componentDidMount() { fetchSomeData().then(data => this.setState({ data })); } render() { return this.props.render(data); } } <DataProvider render={data => ( <h1>Hello {data.name}</h1> )}/> 
 
 
 
 
 
 
 return this.props.render(data);
 
 
 
 
 
 

  29. class DataProvider extends React.Component { state = { data: null

    } componentDidMount() { fetchSomeData().then(data => this.setState({ data })); } render() { return this.props.render(data); } } <DataProvider render={data => ( <h1>Hello {data.name}</h1> )}/> 
 
 
 
 
 
 
 
 
 
 
 
 <DataProvider render={data => ( <h1>Hello {data.name}</h1> )}/>
  30. class DataProvider extends React.Component { state = { data: null

    } componentDidMount() { fetchSomeData().then(data => this.setState({ data })); } render() { return this.props.render(data); } } <DataProvider render={data => ( <h1>Hello {data.name}</h1> )}/> 
 
 
 
 
 
 
 
 
 
 
 
 data => ( <h1>Hello {data.name}</h1> )
  31. class DataProvider extends React.Component { state = { data: null

    } componentDidMount() { fetchSomeData().then(data => this.setState({ data })); } render() { return this.props.children(data); } } <DataProvider> {data => ( <h1>Hello {data.name}</h1> )} </DataProvider>
  32. https://medium.com/@jackyef/react-hooks-why-we-should-embrace-it-86e408663ad6

  33. None
  34. Hooks!

  35. What are React Hooks?

  36. Hooks let you use state and other React features
 without

    writing a class!
  37. Hooks let you use state and other React features
 with

    just function components!
  38. import React, { useState } from 'react'; function Example() {

    const [count, setCount] = useState(0); return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> ); }
  39. ✅ New feature as of React 16.8 ✅ Completely opt-in

    ✅ No breaking changes ✅ Fully backwards compatible
  40. Why React Hooks?

  41. Better reuse and primitive for stateful logic between components

  42. class Example extends React.Component { constructor(props) { super(props); this.state =

    { count: 0 }; } componentDidMount() { document.title = `You clicked ${this.state.count} times`; } componentDidUpdate() { document.title = `You clicked ${this.state.count} times`; } render() { return ( <div> <p>You clicked {this.state.count} times</p> <button onClick={() => this.setState({ count: this.state.count + 1 })}> Click me </button> </div> ); } }
  43. class Example extends React.Component { constructor(props) { super(props); this.state =

    { count: 0 }; } componentDidMount() { document.title = `You clicked ${this.state.count} times`; } componentDidUpdate() { document.title = `You clicked ${this.state.count} times`; } render() { return ( <div> <p>You clicked {this.state.count} times</p> <button onClick={() => this.setState({ count: this.state.count + 1 })}> Click me </button> </div> ); } }
  44. function Example() { const [count, setCount] = useState(0); useEffect(() =>

    { document.title = `You clicked ${count} times`; }); return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> ); }
  45. class FriendStatusWithCounter extends React.Component { constructor(props) { super(props); this.state =

    { count: 0, isOnline: null }; this.handleStatusChange = this.handleStatusChange.bind(this); } componentDidMount() { document.title = `You clicked ${this.state.count} times`; ChatAPI.subscribeToFriendStatus( this.props.friend.id, this.handleStatusChange ); } componentDidUpdate() { document.title = `You clicked ${this.state.count} times`; } componentWillUnmount() { ChatAPI.unsubscribeFromFriendStatus( this.props.friend.id, this.handleStatusChange ); } handleStatusChange(status) { this.setState({ isOnline: status.isOnline }); } // ...
  46. function FriendStatusWithCounter(props) { const [count, setCount] = useState(0); useEffect(() =>

    { document.title = `You clicked ${count} times`; }); const [isOnline, setIsOnline] = useState(null); useEffect(() => { function handleStatusChange(status) { setIsOnline(status.isOnline); } ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange); return () => { ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange); }; }); // ... }
  47. Hooks lets us organize logic into reusable isolated units

  48. No more wrapper hell

  49. DEMO TIME!

  50. What will happen to React classes?

  51. Nothing.

  52. How will hooks affect the core concepts of React?

  53. ✅ Everything you already know won't change ✅ Hooks provide

    a more direct API props state context refs lifecycle
  54. No more constructor No more bindings No more this Less

    code Cleaner code Learning curve Bigger React API surface Not all covered (yet) There are some rules
  55. ☝ Only from React functions ☝ Only on top level

  56. Hooks rely on call order.

  57. function Form() { // 1. Use the name state variable

    const [name, setName] = useState('Mary'); // 2. Use an effect for persisting the form useEffect(function persistForm() { localStorage.setItem('formData', name); }); // 3. Use the surname state variable const [surname, setSurname] = useState('Poppins'); // 4. Use an effect for updating the title useEffect(function updateTitle() { document.title = name + ' ' + surname; }); // ... }
  58. // ------------ // First render // ------------ useState('Mary') // 1.

    Initialize name state variable with 'Mary' useEffect(persistForm) // 2. Add an effect for persisting the form useState('Poppins') // 3. Initialize surname state variable useEffect(updateTitle) // 4. Add an effect for updating the title // ------------- // Second render // ------------- useState('Mary') // 1. Read name state variable (argument ignored) useEffect(persistForm) // 2. Replace the effect for persisting the form useState('Poppins') // 3. Read surname state variable (argument ignored) useEffect(updateTitle) // 4. Replace the effect for updating the title // ...
  59. // We're breaking the rule by using a Hook in

    a condition if (name !== '') { useEffect(function persistForm() { localStorage.setItem('formData', name); }); }
  60. useState('Mary') // 1. Read name state variable (argument ignored) //

    useEffect(persistForm) // This Hook was skipped! useState('Poppins') // 2 (but was 3). Fail to read the surname state useEffect(updateTitle) // 3 (but was 4). Fail to replace the effect
  61. useEffect(function persistForm() { // We're not breaking the first rule

    anymore if (name !== '') { localStorage.setItem('formData', name); } });
  62. When to useReducer over useState?

  63. Start with useState when building new components

  64. Switch to useReducer on larger state shapes

  65. How about tooling?

  66. eslint-plugin-react-hooks "react-hooks/rules-of-hooks": "error", "react-hooks/exhaustive-deps": "warn",

  67. None
  68. How to do I test React Hooks?

  69. You don't.

  70. Test the component as usual.

  71. @glnnrys reactjs.org/hooks nikgraf.github.io/react-hooks github.com/rehooks/awesome-react-hooks codesandbox.io/s/github/glennreyes/hooks-demo

  72. Glenn Reyes @glnnrys Thank you! reactjs.org/hooks nikgraf.github.io/react-hooks github.com/rehooks/awesome-react-hooks codesandbox.io/s/github/glennreyes/hooks-demo