Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Powering code reuse with context and render props

106 views

Published on

React's context API has always been labelled as experimental, but from the advent of Redux it has been used in all the most exciting react libraries. It's now a stabilised feature so now is the perfect time to learn the new context API and how render props can simplify your code.

Published in: Software
  • Be the first to comment

  • Be the first to like this

Powering code reuse with context and render props

  1. 1. September 2018 Powering Code Reuse with Context and Render Props Forbes Lindesay
  2. 2. Thanks to our sponsors!
  3. 3. FUNCTIONS AS FIRST CLASS VALUES
  4. 4. DOUBLE/TRIPPLE ARRAY function doubleArr(vs) { const result = []; for (let i=0;i<vs.length;i ++) { result.push(vs[i] * 2); } return result; } // input: [1, 2, 3] // output: [2, 4, 6] function trippleArr(vs) { const result = []; for (let i=0;i<vs.length;i ++) { result.push(vs[i] * 3); } return result; } // input: [1, 2, 3] // output: [3, 6, 9]
  5. 5. function trippleArr(vs) { const result = []; for (let i=0;i<vs.length;i ++) { result.push(vs[i] * 3); } return result; } // input: [1, 2, 3] // output: [3, 6, 9] function doubleArr(vs) { const result = []; for (let i=0;i<vs.length;i ++) { result.push(vs[i] * 2); } return result; } // input: [1, 2, 3] // output: [2, 4, 6] DOUBLE/TRIPPLE ARRAY
  6. 6. function multiplyArr(vs, factor) { const result = []; for (let i=0;i<vs.length;i ++) { result.push(vs[i] * factor); } return result; } MULTIPLY ARRAY
  7. 7. MULTIPLY/ADD ARRAY function multiplyArr(vs, factor) { const result = []; for (let i=0;i<vs.length;i ++) { result.push(vs[i] * factor); } return result; } // input: [1, 2, 3], 3 // output: [3, 6, 9] function addArr(vs, increment) { const result = []; for (let i=0;i<vs.length;i ++) { result.push(vs[i] + increment); } return result; } // input: [1, 2, 3], 3 // output: [4, 5, 6]
  8. 8. function multiplyArr(vs, factor) { const result = []; for (let i=0;i<vs.length;i ++) { result.push(vs[i] * factor); } return result; } // input: [1, 2, 3], 3 // output: [3, 6, 9] function addArr(vs, increment) { const result = []; for (let i=0;i<vs.length;i ++) { result.push(vs[i] + increment); } return result; } // input: [1, 2, 3], 3 // output: [4, 5, 6] MULTIPLY/ADD ARRAY
  9. 9. function mapArray(vs, fn) { const result = []; for (let i=0;i<vs.length;i ++) { result.push(fn(vs[i])); } return result; } MAP ARRAY
  10. 10. MAP ARRAY function mapArray(vs, fn) { return vs.map(fn); }
  11. 11. REACT PROPERTIES ARE PARAMETERS
  12. 12. REACT NOW class Now extends React.Component { state = {now: new Date()}; componentDidMount() { this._timer = setInterval( () => this.setState({now: new Date()}), 100, ); } componentWillUnmount() { clearInterval(this._timer); } render() { return this.state.now.toString(); } }
  13. 13. REACT NOW class UTCNow extends React.Component { state = {now: new Date()}; componentDidMount() { this._timer = setInterval( () => this.setState({now: new Date()}), 100, ); } componentWillUnmount() { clearInterval(this._timer); } render() { return this.state.now.toUTCString(); } }
  14. 14. class UTCNow extends React.Component { state = {now: new Date()}; componentDidMount() { this._timer = setInterval( () => this.setState({now: new Date()}), 100, ); } componentWillUnmount() { clearInterval(this._timer); } render() { return this.state.now.toUTCString(); } } REACT NOW
  15. 15. REACT NOW class Now extends React.Component { state = {now: new Date()}; componentDidMount() { this._timer = setInterval( () => this.setState({now: new Date()}), 100, ); } componentWillUnmount() { clearInterval(this._timer); } render() { return this.state.now.[ this.props.utc ? 'toUTCString' : 'toString' ](); } }
  16. 16. REACT NOW class Now extends React.Component { state = {now: new Date()}; componentDidMount() { this._timer = setInterval( () => this.setState({now: new Date()}), 100, ); } componentWillUnmount() { clearInterval(this._timer); } render() { return this.props.render(this.state.now); } }
  17. 17. REACT NOW function LocalTime() { return <Now render={now => now.toTimeString()} />; } function UTCTime() { return <Now render={now => now.toUTCTimeString()} />; } function LocalDateTime() { return <Now render={now => now.toString()} />; } function UTCDateTime() { return <Now render={now => now.toUTCString()} />; }
  18. 18. DATE FORMATTING Fri Sep 14 2018 15:47:26 GMT+0200 (Central European Summer Time) <strong>Fri Sep 14 2018 </strong> 15:47:26 GMT+0200 (Central European Summer Time)
  19. 19. REACT NOW function DateTime() { return ( <Now render={now => ( <React.Fragment> <strong>{now.toDateString()} </strong> {' ' + now.toTimeString()} </React.Fragment> )} /> ); }
  20. 20. “CHILDREN” IS JUST A SPECIAL PROPERTY
  21. 21. REACT NOW class Now extends React.Component { state = {now: new Date()}; componentDidMount() { this._timer = setInterval( () => this.setState({now: new Date()}), 100, ); } componentWillUnmount() { clearInterval(this._timer); } render() { return this.props.render(this.state.now); } }
  22. 22. REACT NOW class Now extends React.Component { state = {now: new Date()}; componentDidMount() { this._timer = setInterval( () => this.setState({now: new Date()}), 100, ); } componentWillUnmount() { clearInterval(this._timer); } render() { return this.props.children(this.state.now); } }
  23. 23. function DateTime() { return ( <Now render={now => ( <React.Fragment> <strong>{now.toDateString()} </strong> {' ' + now.toTimeString()} </React.Fragment> )} /> ); } REACT NOW
  24. 24. REACT NOW function DateTime() { return ( <Now> {now => ( <React.Fragment> <strong>{now.toDateString()} </strong> {' ' + now.toTimeString()} </React.Fragment> )} </Now> ); }
  25. 25. STATE IS ANYTHING THAT CHANGES OVER TIME
  26. 26. REACT TREE COMPONENT COMPONENT COMPONENT COMPONENT COMPONENTCOMPONENTCOMPONENT COMPONENT COMPONENT COMPONENT COMPONENTCOMPONENTCOMPONENT
  27. 27. REACT TREE COMPONENT COMPONENT COMPONENT COMPONENT COMPONENTCOMPONENTCOMPONENT COMPONENT COMPONENT COMPONENT COMPONENTCOMPONENTA
  28. 28. REACT TREE COMPONENT COMPONENT COMPONENT COMPONENT COMPONENTCOMPONENTCOMPONENT COMPONENT COMPONENT COMPONENT COMPONENTBA
  29. 29. REACT TREE COMPONENT COMPONENT COMPONENT PARENT COMPONENTCOMPONENTCOMPONENT COMPONENT COMPONENT COMPONENT COMPONENTBA
  30. 30. REACT TREE COMPONENT COMPONENT COMPONENT PARENT COMPONENTCOMPONENTCOMPONENT COMPONENT C COMPONENT COMPONENTBA
  31. 31. REACT TREE ROOT COMPONENT COMPONENT PARENT COMPONENTCOMPONENTCOMPONENT COMPONENT C COMPONENT COMPONENTBA
  32. 32. REACT TREE ROOT COMPONENT COMPONENT PARENT COMPONENTCOMPONENTCOMPONENT COMPONENT C COMPONENT COMPONENTBA
  33. 33. REACT TREE ROOT COMPONENT COMPONENT COMPONENT COMPONENTCOMPONENTCOMPONENT COMPONENT C COMPONENT COMPONENTBA External Store?
  34. 34. REACT TREE ROOT COMPONENT COMPONENT COMPONENT COMPONENTCOMPONENTCOMPONENT COMPONENT C COMPONENT COMPONENTBA
  35. 35. REACT TREE ROOT COMPONENT COMPONENT COMPONENT COMPONENTCOMPONENTCOMPONENT COMPONENT C COMPONENT COMPONENTBA
  36. 36. REACT TREE ROOT SHARED PARENT COMPONENT COMPONENT COMPONENTCOMPONENT C COMPONENT COMPONENT COMPONENT COMPONENTBA
  37. 37. REACT TREE ROOT SHARED PARENT COMPONENT COMPONENT COMPONENT C COMPONENT COMPONENT D EBA SHARED PARENT 2
  38. 38. const Color = React.createContext('black'); function ThemedCircle() { return ( <Color.Consumer> {color => <Circle color={color} />} </Color.Consumer> ) } <React.Fragment> <Color.Provider value="blue"> <ThemedCircle /><ThemedCircle /><ThemedCircle /> </Color.Provider> <Color.Provider value="red"> <ThemedCircle /><ThemedCircle /><ThemedCircle /> </Color.Provider> <ThemedCircle /><ThemedCircle /><ThemedCircle /> </React.Fragment>
  39. 39. const Color = React.createContext('black'); function ThemedCircle() { return ( <Color.Consumer> {color => <Circle color={color} />} </Color.Consumer> ) } <React.Fragment> <Color.Provider value="blue"> <ThemedCircle /><ThemedCircle /><ThemedCircle /> </Color.Provider> <Color.Provider value="red"> <ThemedCircle /><ThemedCircle /><ThemedCircle /> </Color.Provider> <ThemedCircle /><ThemedCircle /><ThemedCircle /> </React.Fragment>
  40. 40. const Color = React.createContext('black'); function ThemedCircle() { return ( <Color.Consumer> {color => <Circle color={color} />} </Color.Consumer> ) } <React.Fragment> <Color.Provider value="blue"> <ThemedCircle /><ThemedCircle /><ThemedCircle /> </Color.Provider> <Color.Provider value="red"> <ThemedCircle /><ThemedCircle /><ThemedCircle /> </Color.Provider> <ThemedCircle /><ThemedCircle /><ThemedCircle /> </React.Fragment>
  41. 41. const Toggle = React.createContext({on: false, onToggle: () => {}}); class ToggleProvider extends React.Component { state = {on: false, onToggle: () => this.setState(s => ({on: !s.on}))}; render() { return ( <Toggle.Provider value={this.state}> {this.props.children} </Toggle.Provider> ); } } function ToggleButton() { return ( <Toggle.Consumer> {({on, onToggle}) => <button onClick={onToggle}>{on ? 'on' : 'off'} </button>} </Toggle.Consumer> ) } <ToggleProvider><ToggleButton /><ToggleButton /><ToggleButton /> </ToggleProvider>
  42. 42. const Toggle = React.createContext({on: false, onToggle: () => {}}); class ToggleProvider extends React.Component { state = {on: false, onToggle: () => this.setState(s => ({on: !s.on}))}; render() { return ( <Toggle.Provider value={this.state}> {this.props.children} </Toggle.Provider> ); } } function ToggleButton() { return ( <Toggle.Consumer> {({on, onToggle}) => <button onClick={onToggle}>{on ? 'on' : 'off'} </button>} </Toggle.Consumer> ) } <ToggleProvider><ToggleButton /><ToggleButton /><ToggleButton /> </ToggleProvider>
  43. 43. const Toggle = React.createContext({on: false, onToggle: () => {}}); class ToggleProvider extends React.Component { state = {on: false, onToggle: () => this.setState(s => ({on: !s.on}))}; render() { return ( <Toggle.Provider value={this.state}> {this.props.children} </Toggle.Provider> ); } } function ToggleButton() { return ( <Toggle.Consumer> {({on, onToggle}) => <button onClick={onToggle}>{on ? 'on' : 'off'} </button>} </Toggle.Consumer> ) } <ToggleProvider><ToggleButton /><ToggleButton /><ToggleButton /> </ToggleProvider>
  44. 44. const Toggle = React.createContext({on: false, onToggle: () => {}}); class ToggleProvider extends React.Component { state = {on: false, onToggle: () => this.setState(s => ({on: !s.on}))}; render() { return ( <Toggle.Provider value={this.state}> {this.props.children} </Toggle.Provider> ); } } function ToggleButton() { return ( <Toggle.Consumer> {({on, onToggle}) => <button onClick={onToggle}>{on ? 'on' : 'off'} </button>} </Toggle.Consumer> ) } <ToggleProvider><ToggleButton /><ToggleButton /><ToggleButton /> </ToggleProvider> OFF OFF OFF
  45. 45. const Toggle = React.createContext({on: false, onToggle: () => {}}); class ToggleProvider extends React.Component { state = {on: false, onToggle: () => this.setState(s => ({on: !s.on}))}; render() { return ( <Toggle.Provider value={this.state}> {this.props.children} </Toggle.Provider> ); } } function ToggleButton() { return ( <Toggle.Consumer> {({on, onToggle}) => <button onClick={onToggle}>{on ? 'on' : 'off'} </button>} </Toggle.Consumer> ) } <ToggleProvider><ToggleButton /><ToggleButton /><ToggleButton /> </ToggleProvider> ON ON ON
  46. 46. const Toggle = React.createContext({on: false, onToggle: () => {}}); class ToggleProvider extends React.Component { state = {on: false, onToggle: () => this.setState(s => ({on: !s.on}))}; render() { return ( <Toggle.Provider value={this.state}> {this.props.children} </Toggle.Provider> ); } } function ToggleButton() { return ( <Toggle.Consumer> {({on, onToggle}) => <button onClick={onToggle}>{on ? 'on' : 'off'} </button>} </Toggle.Consumer> ) } <ToggleProvider><ToggleButton /><ToggleButton /><ToggleButton /> </ToggleProvider> OFF OFF OFF
  47. 47. const Toggle = React.createContext({on: false, onToggle: () => {}}); class ToggleProvider extends React.Component { state = {on: false, onToggle: () => this.setState(s => ({on: !s.on}))}; render() { return ( <Toggle.Provider value={this.state}> {this.props.children} </Toggle.Provider> ); } } function ToggleButton() { return ( <Toggle.Consumer> {({on, onToggle}) => <button onClick={onToggle}>{on ? 'on' : 'off'} </button>} </Toggle.Consumer> ) } <ToggleProvider><ToggleButton /><ToggleButton /><ToggleButton /> </ToggleProvider> ON ON ON
  48. 48. SUMMARY ▸ Functions being first class values means we can use them as parameters to enable more flexible code re-use ▸ Using functions as parameters allows highly customisable shared components, complete with state and lifecycle hooks. ▸ React Context lets you share state across the tree of your application ▸ Context still uses the React tree. This means it works with server side rendering and you can safely use it in parts of your app, as well as using it as a whole app solution. @ForbesLindesay

×