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!

Glenn Reyes

April 05, 2019
Tweet

More Decks by Glenn Reyes

Other Decks in Programming

Transcript

  1. 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> ); }, });
  2. React.createClass({ mixins: [ReactWindowMixins.OnResize], render: function() { const { window }

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

    } = this.state; return ( <div> current window size: {window.width}, {window.he
  4. 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> ) } }
  5. const App = (props) => { const greeting = 'Hello';

    return <div>{greeting} {props.name}!</div>; }
  6. 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> )
  7. 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} /> ) } }
  8. 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> ))
  9. 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> )}/>
  10. 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); } }
 
 
 
 

  11. 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);
 
 
 
 
 
 

  12. 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> )}/>
  13. 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> )
  14. 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>
  15. 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> ); }
  16. ✅ New feature as of React 16.8 ✅ Completely opt-in

    ✅ No breaking changes ✅ Fully backwards compatible
  17. 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> ); } }
  18. 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> ); } }
  19. 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> ); }
  20. 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 }); } // ...
  21. 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); }; }); // ... }
  22. ✅ Everything you already know won't change ✅ Hooks provide

    a more direct API props state context refs lifecycle
  23. 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
  24. 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; }); // ... }
  25. // ------------ // 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 // ...
  26. // We're breaking the rule by using a Hook in

    a condition if (name !== '') { useEffect(function persistForm() { localStorage.setItem('formData', name); }); }
  27. 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
  28. useEffect(function persistForm() { // We're not breaking the first rule

    anymore if (name !== '') { localStorage.setItem('formData', name); } });